From 67e7a17d96043c0ee2fee01fb7f51a09ce113ca5 Mon Sep 17 00:00:00 2001 From: suyx Date: Mon, 27 Jul 2026 16:54:58 +0800 Subject: [PATCH] chore: establish frozen P0-A baseline --- .gitignore | 50 + Dada-LowFi-Preview/DVPM8.png | Bin 0 -> 105701 bytes Dada-LowFi-Preview/WbDYT.png | Bin 0 -> 109110 bytes Dada-LowFi-Preview/Z0pf8.png | Bin 0 -> 83416 bytes Dada-LowFi-Preview/Z8p3I.png | Bin 0 -> 65445 bytes Dada-LowFi-Preview/awt96.png | Bin 0 -> 156247 bytes Dada-LowFi-Preview/bHQnJ.png | Bin 0 -> 130987 bytes Dada-LowFi-Preview/d99Lo.png | Bin 0 -> 117301 bytes Dada-LowFi-Preview/e4e2Kz.png | Bin 0 -> 139716 bytes Dada-P0A-LowFi.pen | 30088 ++++++++++++++++++++++++++++++++ DevelopmentPlan.md | 1022 ++ FeatureSummary.md | 260 + PRD.md | 3008 ++++ UIDesign.md | 1402 ++ tasks.manifest.json | 9361 ++++++++++ tasks.md | 391 + tdd.md | 1017 ++ 17 files changed, 46599 insertions(+) create mode 100644 .gitignore create mode 100644 Dada-LowFi-Preview/DVPM8.png create mode 100644 Dada-LowFi-Preview/WbDYT.png create mode 100644 Dada-LowFi-Preview/Z0pf8.png create mode 100644 Dada-LowFi-Preview/Z8p3I.png create mode 100644 Dada-LowFi-Preview/awt96.png create mode 100644 Dada-LowFi-Preview/bHQnJ.png create mode 100644 Dada-LowFi-Preview/d99Lo.png create mode 100644 Dada-LowFi-Preview/e4e2Kz.png create mode 100644 Dada-P0A-LowFi.pen create mode 100644 DevelopmentPlan.md create mode 100644 FeatureSummary.md create mode 100644 PRD.md create mode 100644 UIDesign.md create mode 100644 tasks.manifest.json create mode 100644 tasks.md create mode 100644 tdd.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac6791b --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# JavaScript dependencies and package-manager caches +node_modules/ +.pnpm-store/ + +# Build and compiler output +dist/ +build/ +.build/ +.vite/ +*.tsbuildinfo + +# Test and verification output +coverage/ +artifacts/ +playwright-report/ +test-results/ +.playwright/ + +# .NET and Visual Studio output +bin/ +obj/ +.vs/ +*.user +*.suo + +# Dada runtime data (must remain outside source control) +LocalDataRoot/ +.dada-local/ +*.sqlite +*.sqlite-shm +*.sqlite-wal +*.db +*.log +logs/ +staging/ + +# Credentials and machine-local configuration +.env +.env.* +!.env.example +*.local +secrets/ +credentials/ + +# Editor and operating-system metadata +.idea/ +.vscode/ +.DS_Store +Thumbs.db +Desktop.ini diff --git a/Dada-LowFi-Preview/DVPM8.png b/Dada-LowFi-Preview/DVPM8.png new file mode 100644 index 0000000000000000000000000000000000000000..50bf0d3298c54b9ff1be2a2f8134d2a12a2fa3d1 GIT binary patch literal 105701 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 NsVZGlERr`3`9HlrcJlxL literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/WbDYT.png b/Dada-LowFi-Preview/WbDYT.png new file mode 100644 index 0000000000000000000000000000000000000000..cce83eaf79ba7ba01f654d95a043b4dbacb6de87 GIT binary patch literal 109110 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 literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/Z8p3I.png b/Dada-LowFi-Preview/Z8p3I.png new file mode 100644 index 0000000000000000000000000000000000000000..9db5ba2fc975254aacbaac533e9e1a8fee2c52d6 GIT binary patch literal 65445 zcmeFZWmr{P`!6gAO3I=;7TqW*-CZIL64H{=ozmSM5=wVVi*!jycXxNbbK(A<^E~c- z_WSL<-Vf)xrm}?Tnq!S|kNf_`JqUUuCytDOhw$Xd6J$vVk+)Btz-B#p0`(Q{8StHO z;gcKS3zY3!aiJ$Ag9KYoo{&6|6cJQ%(%wyjPnA8p>B>lYWrSlkq8D@>*WFBhqkT^5 znu&%M*A$zB#!w?=Fc8fK)`Y{PknUwd;RW+=S1TnnDn^zCX`iL!zut%el>l zf+iREw+l@6Cg-Cz!Z0|yC;Z;*Fd`V1O~@%i`RsXzmx>*yQ&_t8;{oDh&eq5aEM z7Wc%@MDwV^_TO$IfkGJOdH(OCHm48(PpdTUrvB@=y@XhY{&hSgP@3>>0&6gtxT*g2 zK46M$|8-#`TK|7IMUi5GHTJ9ya{m@6iC7Ru6q)tf=Jee!Rs z*F6hEEn>WXx3T?i!P4pgi74@YsDE2#5?l1optSmnjKY5%*9>F{312<`m$)PG?w9ov zvRj|`EJ^>jh%u)E!ph+X!N0CGGY1f$Nv;p|F8?+U|97$;C-MJI*6#@a|0uG$d0?(j zn{bXh#6(0SOic^&J@!n^Oh2NBF<;-x(6{~iQBrN->rwnsR}-z98fKm_g&|v}V}}RW zv738KtN*YWTh(EB{7kH}vM@7;)eASwA)A`y4(?5$9RE2%(m(Abl71LgFjqDr>HE&_ zOOSVLWrvol-)AP9u2J+NjCled`Nt)&PPT|4NPe=1q$&eV{;>(|W3PRJ(CL}WGaIdc zsB)1nU{Xl~bDrY8kc1?*8W3>%#IVq5w?fcJjh0O-eqtmDY`j9YYA)oP%1KBg9xl?m z)E;HC`Z1Q^HHgOZ78Ud3f&&2#b^`4U%8%z`_A}$0hCAc*P=q@NpJ(e84KLGL;1Kp%cj$V|Pm0{WcLtYUU_nH1(RMl2}kHR=Vgb45hKuFC{}$`O=zw zOhV!lR+}!E)N}#^Wm53yN?Wk5dy|}xb9zg7y%C3vuue{f6pmL=)C~rbO|G{4OjrUY zr;XMMU}%)Lr3Cb69jz>{HZr=s!q)`om70hHR;29e&Iu}iU%l^CVOG(|%=&7P_j131ek45fSW%nhYJi&loASN_DH}vIY`@LZ-oWt;i8S2(`V1W>(6~H;7<IMS2}Rd9je4qaKqAW=iKc{Ux$>Y+xf{bbWWBo^#foNf)0m>}w+6vbMI%Y-=PV z;q)QxvjlmtXqJfq^>Y3`r@>jl;iX1^Ui0p!KQkQ=x4|TO4!q{#j}w~q*Mt^dQw}8( zkx^Sdk61{tl8b|eEs^r}QT^q)vg}ja)GAr;q@c1jLsfMS(yU2~)3j>ZzDNx?oX#6I ztiLjP*!wBzXXCuO;Q5J_>;7R%4KoWo&UCk%`~)AJ>}}p5+x>LLo4a<)5$c598Eme? zSV~#Oo`4ue9Jzx5)Tz~u1V+4?PvWeNS-5{r&&MWIV7N9-)*CLsYA`cF<-T(yWDwD; zUeD8FsU_fAo#V(nB71xhP z`mj4k%*1JX?fJU=w6f@Uw$l3eY8!?|{Wy3jmH0!3BD)2`z*EHbw#i4B1BvnfxxS0E z()dUue*B07iN&)22@T;S)V;k>YVF(LMvi@-#TJ$9K6FllyKjrUUM*?~B#sMvp117* zWM5RmW~2EZl&9`j)Z6p@D!agrb|dB zV)Qflbn=>Av!LjTR4^Ee?Oc1@vD!it>%%DRYqLNfzmi_ZlrX$*=7g#`tJEu{neT;H%&ms@M9$eN%02}xk)eIjr&`gAI@?)~ zUhoKsb~$6}JH_q%einotWn>PY03?OH4P-BGbDV?C zShqz(*Jr54BI-D#eZd+D&vR+0?+U_b5hIMxha|NhVlcbOke_^)f|L$~VM_TBhrO7!w5T9TO&v3KGnG&zT!Ol@@5>Y$IeHZO zo~zrxZ2ELOwH0|&*qg}FDe;V7P*I3VY(+c6uoJe4y!PANG>I~#Lt9OE4P|LQGb>Oh zt0mwx7s5P;hQ1%mVBj?lH2GCfG?IPe`Tf3PZ6n8=bEWA<_gzt_8&%5t2lKxv0dio- zFll%=0ebQDjV&K1-E`5j74P;2Hj~*8zNSE46Sl#hv=S~6UIa_q1v!erN@xx z10jv_!OhS-*lJ#Bs-Am(^HwRtaXV3%-4brFhSYIiX{u>r)(K}+s$9q_gjyM4Y774B zcrF{>?_AJ1@=9X``HKnx_M{o{v*&d|DDGzmSUZ%^eaN0Mm1FI)}-p!F0FT_>LPfO>8+X#d9dg? zbq6vde(8`)1fg}Rw^J7dK)bey)cKk8pQTt0^;(GbyE?QZ;-^n~|Cqo>Up=1*Q)jnK zls-Rr7}CoY8|Y&;E-9C1UH27=p?=--=SK0~0g5F|)Q&gX#N#}Z{56>h*-R2CHS0|j zJ_CVbHAg@dhF;LEJUUeb^2Ek?2=w6fW#d?h?bOFa@23@gREV>c?mF%a3hA4AsWcwz zGQP1)DdKO7)pZ&MXi~=srdyes?G#*iEK)6ssviJsrBtlfL`~AzHsSsu)jbVBw1!gk zw!Rsc_fLP3g z%I=7>{zCiYoIqd9IObx$nv!|F+gkQ;&{U|!zZD@iX$`r zdNGzrh_x|csm+`scrHmbI;`5cmNCPfQSuoQD}JgHU#MSj*{43%?BSBVCjs8oU^Dn?8I@*4kxh?HM2&D+9p z!z5N36-LVY1Z96t%UK)-{`&#`hzy`Z~ZUDI}ER#>yZDj4h5)o-)5)?)GrH{%dvHL+QIH zWskeFX`+;+nD-BvHhL47Pz@ywnf@Z+Y0_b;2FgA<;V=f4Dog83x0(bt&A{ixjpQqx z_}B!uClWbhdFkw<6T$MV&1L>As7~dLr=YC1^Bdy9URjQ$+6|L=`h?wi!}|oN&~i6X zpI90*^S-0rzA3}?{wks|P&wELAC`IK{gTZx%pPZ`+R-0!op-2wE2E&n&V}(W&0NgB z+$I!{y)zN#+n@)L726Hc)nRcPiJoZH*gXnUI>FB(tNlr|t`}W>A)hMxW;vU8-~}f| zn3Ij|4&sG9ZoyJD4^Hd@##-s!;GK!2(vBQ;FZM?M1cKL#59yfDd*Y*`lxfNZk(&F> zH^Kc$j(e2>MEx`%%ZuHRgxZB(%BjlS>qd^^x16<~I~79W_n|KmcCy!kx@@~`n__Yv zo?O5b7i(5Z%#2S&=aZa&=R>zX->t6ot9H^C$+Fu%T}b!xOd03=;(lY$a^g*4+H}4D zyj=mOTAu;&fX)WiK<8?DHTGkvTwiZ(u6#VEk;IPd>4X%j>*3xEljLbu?fpIa ziTgv7S^@}p2_p)CG8E}%StT{;7xOKjeK%d~ZF<^1?d#Q1Rypj$9Px(8Tps$26}YP> zvky|ZUWf&$EFf52$CpNu%BHMFH@RlTzP#`F1~XN8Go6k>W23z8X(MOU9~hM>T(X7J zyq-OP=;~W{F#o`ccx zo)qh>)(+IJrxjdY7O|{bDj?v)ppkG8HgPNKWJsy(4u}~nTw&u|x^?U-2jnZruKOwl z>^cPuDBo@~cDan^^>%ai!!Lok@#W>6odZL>__^9~M_9r-vayWv$tPJaPgZ|T+>S~I z60w~7RGC`PkDHxzCIp?YnHe!1k`v00g)NWe!{w{Z-`r~)1>GCzSJ}C-7B>Y9U*7&r z&t}H^y;!WfwDZJ{f?~}vvV1s>vUYPdcSQz$@wB`d;W1OTgLF$D&r@xo=|XRIHlnL{ z(8qXj_B-7C=7j7rzpEOKIvU4OJTKzx_vZ)W#e-E#16fTI=~jhK{Y$e&NAarY_2KT% zwzcE6o0ehV^)--lYW5g=x)rd;@F$bwr1D2pS8H|1;?`hELE!5Ro#nODB)GVxa4!-R zBBwmBU2_8wvJ;ny66z#gy3T0O-I?m}am#~N)c0B~%%W{%DJ#!M%@ls zNAD6nCG6W7V_#^}`fVE6O9<`^s~f8`!7B;wN0-J9N0YA!oe=kWu#VWFbkI~H_d}{n z@CS?xh926cj1!9{pIv8zVf>Cbx-9MYT%Pkn6f)UzSDb&Ar<|w1XS>QM^Xl4c%5Zwj z%qp2w7%ypLZ+cb9ijDtHDK!)?hB^wB^y#&+s`>~D`w&fogHcpGicU1C=zXQ3uNCwD zK%=^CDi`LK5i**LSd~fw9B#{DB|HATugRrww$5a9f0C=Vxf@2atYI+%V{gmtCC?uE!)(OC`a-&fm8_9vwQOMI5C6-CMiyoTL+gX77*RZq`W)ZQ z^#P`y=je=iwwvS7`Mo8W4-Z!x>RN8`OhdFS#G$c+5fq5KZ_iX-8ePQgNs!<`J7_t6 zN{Xq~rvZ`{-fzuJlWY5&)OoEyl6$3gK{EE7Fq9?feJF}FoDLL99)P6_g zmLQ4J7OFb$Fmo6jv%dX+PM@qo$uG*a0V!OJ3xs-!4&&N^YoFzKO^p{jrV4%{~euIf~ zfq3*-ew;KWmhZ;PUCJsiD`w^pHX!gZ@Y&zdnIHz>9Mm{+ z@f2c(*&NnKgPm&niB$wQ+Pe*l{MM%B|N%-GusWo@$(Ho=_r-WFOj z^6Eqn<7p+)@ZtC~?!ZF|>926-SVC1_NBa|rvK#X?YwL5SH~n1pX4{_nsp|Al7OEuG zK9fE0EG2ljCV8wMcGM!z1x_Q{jb-2)H72_4{64fOh=>Hb%ss`n%x;DKxJh^K_v;H_ z*3}<(PSxQc%CkJBiBHfLVl@|!#I#`NQ;$@3f0YF9mLE!_|BfEbUAZ_~^Ud9as!~BL zX-ItH2}akb5|6Us-(Y17{3J!-C0bUshFn>#*8nUuyn*K(CQTaAK&CktyJcYfWhE-@ z65Kh#q0;y~vp+1=zD4z9<5lvec`n;?b6ORT^v~OTSJIZp4n&ujS=_6UE@z6XQR2}Q zn&(Q=LRC@Xc01z{k|6;$1@xQRL*zXtOgXZRE>*&EL7`I0(EQ9*;$i)i65F5d z)^ZW`f&6FY_7pWMYU@Q?ykSqob+a+#imeKa5S*ftCR&`fZ`Vb*6Y@x-XJ_c5BQTnq$fZI>&ZI z#@{qF7f_ir$QzW#ty&bE$iM1voBN|7`syX&4261sWgELb~P=at98s$QPB^!g{-4$ySXj#wz(;0td`gSV> zGo1&Orf{5`TA@X;i-6gxjWb_`p<^v!_Uj%6F59(F1$0~8!ox=un{|n}wojGP?fwZl z@3Z?%2UqwHb$C$CerQn1G#YVCSBk$7yerP|439QWmyPxw(uwT|sVJ~Vab3x}J(2gN zRmq$376|m$<#YSNHqaUjLiQ%~US%~9sdb}HaBDg$+GK+zPZ4bK2F+EhQEc%qW>r9W zi?e=$842;ynwWc2+BGc1%J{MFXHwW3U`K0+iLu_BvA-z-VasOY9QTTDP22O5_x?{W z07Fk+QqI@lSn70fM#}30bRD=$y_e`tr0@syg;CnL!;3K5M*73K>R)tp^uc)PZ>h0@ zvZBOZSy_f6xwK90>tBbSye(}mFAF<5RgMVj+}QY2#)?FR>>z=_<~6K~ekF#L8ntq| zUKJ<@)SezxW;3JE##g%<4y?Nbpr_Yel1fSi6&eCiIO%op=ILKw-t!-Dn)}l!(@uzWo(sFZ^Ds+vWYaUeRebrf z=tsj{C^MCLMaQ3>XV3a>(yNy=S%<@k0oaB%1qnZ$+NIQtVGG6O36aN}z`;P(%zh3k z2x6j?gzyx%tG8=iY3nRr!@R+mStgkZZnalM81x3_lwo=Z)CIa2uU?V5o~NPSPEeCr z3cutySyfq3b%7uLq}@m@f5D2iS6%;oXFDz-OjO@Mh>UDnhNK-&UNj6^zWzw`10_~} z1S*Dv?%xuXB79CR$=j+;Wlj&$?=P+Cc&ENc;MHJiW%Ps1TI%jAO(2RNAONY2u9}G*i+o=eg?d8A4T<8l*N8TB-(f3GiPdgrbBn=vWb)-WK0~f7jv^23uO0 z7{qxpSg2~(55p6?^u?TF1Q3}_`?8ww4615g=`%?bj;~kj4!C%(&>SXLub*R~KpzS` zL3xJrXGAQDK;({*5Qh1~VofGd!)<1W4nl(Qm+WC(y9CAdfNtx6y@@A(*Bj4%eg|Oe zp0m^MCQ?qY&xq8Fgia)wM;+#-O`I!2s+qo@qGjanb%m5N<1m~mqS7( zt`%9c*Kb&=S1G)NI#;ztk@WJ&XUq1v9|GRI6}_5AGnb%Le4}20j=@E8`L%$tXz;tZ z0L>(0rCUq=gAaG)#R$Whv?_IWQ=Bxa_sK)CNnY5K-{o(OT~$ldvv^ASe*jFxzk~Gb zP8X#N+emMEOwIX@%@+5-=vp5iqGbK&1+W;vN?9`aUJjYER=L0Y1-S@4KPIGac}Rx? z&-yPjJbFEkpe}{liMhx`BKEB-OyitgUla7?myy%&!ME)@?H8tt$-5ACOl3LIx$* zde1C#DMo%`#U_>uV633=Tg#(G$c@(%D*)!_hwv~L9=noch8vuN+-uyk4A;2&X zir}S#Oi?3u{5b*C=N0gOMg;JBO9moqbLlT1LVf^Z{;kH=l&xu{%fx4vQfj;w%K2>> zNn~^Z9VN8f4js}WzF#aYEN(Z+I9F$^vSh=eh=i(s^&7G@k3G$3p&sHfo44t}NF~9$ zJ5`Kjb!nZ{TX|w?FOfd}+GZK04nHERuE((AWT^Al! zx23CHW>4+V1^sc)I1fXP9 zqNZFDs~Jvw!LVtRqZf7E)KUM*Ht^A``_E;NBZUaNk}{mCki7sj#|&-=)cKlVlxbcV z`jFgANU>U^N}unaX?TAAPamA z2f(ivfYye8z4omy)C8UC(8q5hM+(zSy{^I0Flv3^HY89sMLQZEohdO<@G`~iHCScd zWwcIByUurWxudkZR~bIKLk-{Y)0xcBYxU;YmXVgn+%P|V@2@ZZR#CJ!VSvnd(17IW z(MS4J{XY`@>tu-MCoa6c3ek!M#Q!TxF(&aH6}gCHM=ox@!p`w{x-*UoRMj3J+aP{k z;j1G-;WMzj@0PpYrVF7hdY*Jn$k0TIS13cl>5~HJ z91UjQjn31GIfdJw=729x1`qFz*ba;MRpXeAW5vqCVgI)k_@B$QL;^7|2b~+Dw}Vvt z5?McH2#-Cj5N-Sr`q#g9M2kU({~RXdb>#kIR|Pk<+@p;p1bA2w zYvUVoz@d7}z2S%H+X_}QBSyuknXBX5*4+wSkI)6he52w%X4mkm<$yTa)%X}HckEdf z+j(|#J9SF=SN!YUjuk!6S8lQUDun|fd8*39T5`HdHa<(Vm>=#*i06G!?6+!LNxiIx z04Uq_^aMP%0mDWn&3NVXJxt?g)eGz)@u@LVas8U!v31-LOI$wb7~KC^0wAxwK@cAc zEo-gfu|h~VLei3v2KahCrL?yga9`V?C+;=NM=f?jX&4V+*OWuz08Y zLcOlLlMTJ)9*+WfdyRQ?%qk_7Q_=9i<7BGN$gSiPX4XO7N-zd>BrPn0cGP#j(%w6i zckyTxB`{4Y2B>5ylR9c^@7JL|B?VpVIVxPA$}*2CRc}p^$*GheG2+jk>Y7XZxpf2~ zWdm)M+4(gjCJb=?s#`<=YOeZ#@3d}{Hxw_OAhJC+$RPf3oKa7J3a-#sZEXECEz_Tf zN+uj&z%e>jHgJpy|MqK|V>5V9lfx4lD%-d|zH(=aXdcJo1vr}Wq-|TlN z;o;#cblad+Z}6=e739*m*3K?D=ADG&8MSB^JRh2_8{gI209z(Pa-rE>HVoT5d&a6o z3jvGXWOAT(!NY8lG8VZ&rI^-Yx+==!?%J4PGcb54_0VN@u&2r(xC4TI(?8U`k7aFMzwB!^F` z*{W}ytao;Q#ukc+3$Je@Cg58#$_AKMg|)B=kys*O%-&!A+2iJY#E>9p2RPEJk;iz$WkhF3QeWVbdfkhMIM;9~RxkwZ=*zq)+&}jx#fL zw{Jgy+tFA|zvK!r=nX_9y-wI(vi4ghB8sf&LP_9~T8^bebDA zqH_VHvMN#Ua#qlI_#Lr?!k86|M|vvDF-P8)z@#d~%KROl!;;>1V=!(b-Br%xY7KkF zZ&NCcR`TxpOe^$IKzBGx^mszYvp$V>rp6+swE3DgaCaFsISR((gdO9NOZgGC&KL&17G-T15IZm^@=Y975FO^ ziY77D8~EDHZLj{alef-|@CG$M$`vR}zk=kl2@W|d_iZiT0XQL6gsZc?G_B~EFqX_} z(Fg*2!RZMlfK=}8NaH_BcBFt9CFq49Imj$L z!{b^KKA}*h*akf-3Ns}1g8>FHuZvRVKpb5HiJ1zb0XdUqO~2uQLXEk)^X*aBToLE_ z{#**BFo)8O?-5u;fB(|cKo5Yl%m#sW2lHBYg8sF0xWn@NcU4tp6LJIXsw9d8Q!9xn zTGlfbwLR+tDX=$BxZ{AYeLsTG*Y0l)RFi&o|MWN=7XKm~@S=p?;qINw=_VMHP6b1) z%EX&~`=e&cWJkcA5g+tY-M+sMt>5MoYD%gQz*rIMY|4-&0ciwnxKK$xK&`X9e;)$n z8v3TQa)_eG%X@v}iKpqMl55B*MivTDXmpg7ag1_dk-CZ+16IAKXDq8_B3-N|Nn(2m z?@=^&t+QdhVMr>_(U-}KE%c4r;csY*R4;)9`Uo>=*a&)l7-W1lT&^!pp;4mUG%Xuw zIbD@+FqE!^#rSU1^o8~HUhRS$3r1tIqN3v3&rIPDe4h@#*>1?Oj8@J*Teg*v2}NtS zs$au0#D^Bd@!=VViNee96Atuuhi5Kx?*RS-g>*c94Z<*v=1Gu$9#X00WS;KA{pAYE zRN4DaGAZn`c&sKQm$Qnm1N{1y+5@!4>21pr$6V>PnK#WQt(I0OYmVQIPFx4aHNgK*|7;-rp|FWR(4jOh_Bcdhm>PZhX#-WLNxE*$1x@))IXG=tp9{19f zUebqePT`tU(yEs1ULO%U&VqjxD921y8aJ?=QKsJ{Pkut-qb}B{j=os%)bY4ndaigP zE@qeKXY%qH`lOM7S}ocka#y-;1Xv*Z^Gbq_M}9Df==DbdZI3I%50hn?$1&s2EAo%j z$BWe0hWH<1fppQAo8dwGMLHqAuI_c50@s#nrITL5TRbUi8=sr_^}NZkg~R{ z4$`UBU-FA#G4#@6GphA@jXOH*6ssEc)l`1nxY^?vKHemoXNi(^T+Uts`U=pnjrs6v zH75v;JCduCy1F;#nkJfF3zXTOIPm;9{8gB}wxa&3!rk?wdRr-yY%EnnGQX8>OU06z znfingj@@=Fy;Z0LE08oe?Wo_HPV`%RKTxKrqj3`2MrJ2lxp*?QQVzP z=oGGi-Qwv~P$gGf?V5P7PX=IoP1LM5Lxfz2p6eK51hJY8KxtxFxCM@AcuTUJ2;v)vEx2FI zm8>5Q^I0$ZUf)EKiH{Mx&pNGyG4k)hqf=5t;khEkAI^%2aa_BtCmHw4c>}J=+@q9y z_MKY96?}a$EW!DaM5_6oR%5&*?8WPwsmdLyZ{&T2rAgRC3oa}WTEsr)T5_GjeAdYG z^QRm8{&)F`Uax!W59@k>TD`jiG)%=3d-ju>wFJh8;R{j0XwqN>!III<1wT|xAvfHe z(;c6Qo$w8B)&k9R$2+A8Mgh9q|0R|HNEwbBssT2tShKc3MjI}lIyRtxqV3c3Li=!B z7BO|>(}RVDop0h1L=47*sZkKTmz=Jc=yfY2}kw8tv{EUnG8+ zuKR6XUP>gr;w{D$laVO^=x~q8>x_&QNe3Yx37}D9I%M=gtQ*owew@R*BZlQvyT871 zbC54_b-B_?FVHXGt>dT^d)0q>wo}=gK6jp$ZM5Fc{%1;BjPOeHfot>ve;2B2yjo9D ze)h6LU7_m5R25Lpu?-l6&CjtwsZj%k@U2F^n{SLn^yLo0gE3SQXo3SGKz5;^?Y-ms zsVc^LeG(75YeC>7mrXpWl!(k-#QE|Zh}B#o>Qr8{eTL(P{cqB|B3_$&48fRmlum&^ zzuB8z0SIg^`7;TaUgl88z>!J4LIi+U6lMH^Vyp-%*Ib_cHhSD>a2!zFr45&GGShAjGhL8p)CQK!ASf!I_PKK8O`YD&HL9@7+7%d_eL*#HH z&=F(!%V*iQ?3sGATfOE)H+>f|Pti|kJ@z6EBF|fEd!$`O9CDyB)Ir-(xW`Axj`~9d z)1NU9gnjoa%y)1g&%^D>NWLOf+;DK#C+P%6Wfk<~HUC-$uP#<*ttjPUzg!SF$Ng(ON6ejE@`RW>e`C;#oqLWw@4m8MswwB?I#pMQB+U?GNj_sTNWW8pd`8^ z(*GO_+p78MYbuZPNKL%~3>Z&)s+&?=P^egf-1jByJMX-po|@}-hfB7;c!V5!^B7sef>G{&f~ZFsk{Z%T z1wxKG3(m61mCsP|dlrQp=Jgt05X*1-t85Bo3cH2P6;1+^jW{4FM+>OYS{3e}VKbiF&RJLjI0+5d?uakB^o@X=O^aw z>=Ayp1SPmPT|G(=TwncRRP3d^g?zPGz3oOk5Og&1<{|*Ah!gDsa0G1gu*TwhUzI1L zMaqG{*vfd}eUl#@oS_&YyS$)^$4>n}sDJklm|c*FWy2o3VZ)Of<6GP*S^& z5bT6!$ar~3Q<5>1`TPJ=k*%ZyS-YNYE?skqNW!H84DlF3CeF5h#kGcnZXjYa=5P|t zcY*?=qGqejR5cw4{Vubzv)A%-)8F2a-P{1XMpcNeqTy(Y`=BvIqe0={Bctb<Gr&jcc#&u~GSt>6OrVv?dbJ#36_8ynmuTb`jcbo~QCc&My3M34Eo3NL`~=7*N2E z-f{dkFx=--ZQjBC?dB)&IsA~%wNzo|=8Uy-pL7CKcu}#C`fB%&y&2@u#j8>2M=karx6J-XqbZkhoMA1f%8=aZH*VFK!T#ea*j*! z5MXe0&c~}!Juy@%rVC%Bu{rGl-nzr&H;q1 zbnQ)+x9@Gp^Ihd^?*Jg@YbT0*HkbXjM7dsvOsNiji);$}+j<*A+}9sJuhGb+zE;q7 z-DBf)*iBs1-{blWA?JWp(!Sqvcdpsa4)hG;dJDWRZD@m#<(H>U^#3$`FMRKh2&9Bu zc9HjYH_~6E;*cDd;x#R!fa9WcP*0fBObV zFI2NOi7%!UsL%#wTYZpQN7iiSG!Sxf*GN#`2*NYWc+uhF)|r^bHzgk%{Ou+ zVk;~SEAy@gaX^8_m z%IQvtMsKL32jA zP(|{5cZvcG2Fnryx=+JmTU(w&_ADPhmt7v0F>B^kIQM=efGc};rz*Afw4IhgJ{wzoz$#fG) z-U670HK)>OfL7QaNg=5Rl9g}XWegx>+5mcu)SLU$QF*PsboX=h{5P3CJV!s!wH+0h zM9fI(ei#Ohq~i`ZIP66OYC*oosSrREVxz&7?=$}=1Jan0rHy< zn@lpRh*(yOd)P@=vx;bz+|B-gn%t=S8YuysUFTGAn+OuhY^D(c$noEO)n;0VT{5 zB4t=NZ#@kqBPBJsy*&ER+1U<%`#w_OH5{b3;%pY!D#_G0e^VWS6d)th3g`mt>?#l< zn-xrNWH1a++a9VdfC12z<7ClRC<1Ak{$yjwu{z*2G8k{UDqc3k0fd6jZk`uD3{6T8 zqYBpxB6r-MrAc7aLJRCSY0>LI5UgQg4w$Jnqe9_1^r~2xtg%Qb(rOql1l|1Vwe z;t;0NP5Cp_#K08akB@9xbB3wq_AQBn=h-h+F)g6ewk*zza6*flQs!TZgZD=`(nDR3 zl`xLg=9_(%oJRW{6$*)sp$r|qt5x)0_vcoi=`p!hVyTFZqBCL0yW%h&=YL~XrORK3?$z_Av1rgmiY)bywVwlZcw@a z5TxxP@2Ah-xvQ3HYXyz>^zHyl7vN7H_WGMEcEzSo^R&X08DxGb2*L6(J|-U#F* zN_gLuZ)P^M9w`ceFJMJ~pH}-b4}XS;z~0oMG7A9C!FxNDz?4zp+4`z`UsOQE&+(-> zRn=sjb#~+Fh%^w8MI@j@loIvIubH6Yv$Z}lyey}#aKbnzGvz>kW9ukV(bYIrZ3REj zz%ySHr&lWx#w2w3Dr7ETys352cvKV;fQE<5*aV-Q0tAQ&P~HlTRsD-$VN5@cLC?>0 zD+L^846|evyNCz}dI0n<$FMJ6%B1UoOq$611hnJ6ou4~qTo#-OvN%UFY$2y- zILQGKA_V{aNExJN3!uUUkU+QLq&Lq)<94QT+1uQ};nsAk&DUCGTyySnNySnxsa%%0 zT^&oQjWK6|$vw`ejA`|+fJ}MZfvqC}WR+NwLKL_@AkC+wpploy499{yyot|_aR}wF`tpZ*U^yu5 z%$t5qz0&9kBMYoH@4S(QMo3sJqBi-~B)xZY15giC-ETr3?hZUniKzJ|n_L}heL|YU zKjEN&r({~m@Ml4XUH(HX%!3ic{EQ&=i`}V}?XS9RKD`%sxYMA6sI+3kaN)pbEI;NP z7m9b~d7cV28?7ywSpqG9m+oeK3)g%=n~6hQc~}4_-5WYiP59ydZf;lMC!f`P@h^5B8MOt%nG&o9!`H2d}?Usozb@bv;(PF1km`&<sDSEj=nM;B*+k+w;PqEO}X95%}}W! zYF^&{RX9?r)57YSYh|SzShI{8R%EPZ;>8bRZ_u^UI1JE!a0!R$m_+mawb@zO)1<|! z^)j293X3}UN%uMAUoO-l0m0-qf3txg^HHW1=>HSGFc?5dxtYi*AprcLc`$SjCF0eH zgfhb@i}6YfHrJh07r6BUHR*S~xVk+Vx+8j#iRRJUUE%i=2|qkvG4~scF+s$#Z|%HG zAsT=2q#4e@%*+O``F-drlB1T2L^lT0FxvslzyZd~Ap1nH^IRTg{F!tVphz`@2T+G0 z*$tH9WpN-#knRwuk5QYz4Vk?B3BC8;Hp`jrMk4_iJl#F`qtdE>1@Kl>MX{r{LWP2; z`GR~BoZe)@GS3q`Ekbo5Y)p@Qk&er)1zZB#b(slasLRMtOuSi7ITf8{sDXp_|HnMI z2v9%-(y|CIFE|}w7C3U3eEPN3Xw4_f+V)rssz{}=)BEAFLrwM;-?#gJ=?U~F2|}6$ z6tsdOlYs`@u2_2Yx0v~#;E=sl5&PlUF+sfGL%n?vrvk}v$stBoR8yFY4TG*K)bL~q z2w4TBy7UlwNGT#*)}g`HVjNhn))J2#wrg=ID;)ZaDzM?zt5?9$9L>1ueWrf6;p-JJ zHNc=7=%sakK^+m|?pZaR#k&YBGGNiGeIBH`v^2|v9|kIB!wk4Tux3ZbuqBg37XVKf zC!^Q8V*kSBchN+2nY`)W8DO?&Ot2hs25(?8zMGVF^4)j^V|dA8wj>KUFJbhC21;g` z*ja_jg_J;CD66*tEWGYGSi8nNsn_>z@e@j;hAhCGbbxwDQ+5p;u8X@p?qe!2gR46v z6?0$=22Z1k;SS@VLz~x_PmLIe2+MSGfmca&2fWXAL>&z{R11`PC`8DFad;LN!a}WH z0$TjHiltT@ptLBGvcb4DF-U$+8c4)uUWv*Nqc_`y7qMc{<%CAXTlNH){@gjH#o_@{ z8@{c`D=~&!_+iiiP`jCG=NwYFGDxb(BB5T$n@=V!vJ?@z_5&^~YT3>={Ov2iykS!~ ze>`veM@dXd^dj`>9^hCgLb%SnBr<2O5_rXBz)eO%2~qc)33i_Hb%W@ZE(&3Nl>-N< zZ=Zv4VJ{=UAih5XtdJ6iWKdYB#wk=LrXG2PRkarhA}Yo>F-ZLkDw6{MWblJ7<>(f= z3s8utaGiiTh;3`A)s|h!s9ARd09hev=+XC~0Q@YDNls_(bfGFjr75a^U@r!5 zAqq*E%TL5sqob_4%v?D0^GV&AHh(fOxXqY@(`NN$RX&`3sQ?l+DQW*O zlqc|B0%DjVv`Tp-Y-Z!J7xS(|)e1v=H$}Gq9jP$=MXSki04T+gMe3EkIg&BA;fgsD zs5I~D$`;tg!wFDthqKJ5Dr^8%D_^BJ8G}|y;;T#&DS+x)079Sw_|9a?1xk;sTw~!m zu^WkgXJ{1AFED3y)98ExSUruT?lgaFXm_QyT;XX~X}waWGUb~fgH zH~H@`g~N$>3#Q#Jmja|_*@6M1;S#e_JQN2wx}zNj_(W4ys-K>tU1pp?1-x5$OUv>__B*f_iY6T%c6pQ`J57!AOv6lQ+6D4V; z4-i}Ij)#Uuvc_s*|5`nQh&M+m|Lt)<%aBAk0cQ??2e&JMzaS(S;*#1deTO~bdEvve z4y4N#CoSSmB0je|2!Ds;VVZWn=FQ;h+I0A^Upy7O1^zg{95W07KPC z#%}&`ltZ91eR=%DbD$Ggt}!PT4`rO=g_7 zhOL|3t_*g@i%qov{`=N79d$DIIb!(Z?|}JVk+>xb_VwIogwaJ!-rAI z+98PUF|iPp&+T#R6qkQ<*pAZR@nY$Gw%*nfw+>Jdq5z00QauF_#NN+*ZkJ`(u7J(3 zHm+%f2{)txVQwlV@3vHV0EON07Y?MHE$jhp=U-*1hYZNx5OGxW zNehV*;ECn19`>~Yx{~bK6a>vOT|OsrYTer^ZjJ%e6dJ8>RZ2x_3hv%3_Qeec%`+CM zeu&t;it@aMug?MJa*2Hq2=uXp2f(s4aVjc(#?*BBHkrweE^P@r)^hG!{fjodqnue& zfz&SagO9%V*kUXz)fO`0@!gbqY+%nQ!z8!W36 zKadt6ZDj8_UmA2eZQ;00M9yIlmK85nezNiJ|L=nK^M*_@75oqMuWXJe%3dq@c zo1VnIIU^LB}Je^F1^98c@3ob|=d}d}|+7yM;Ft zm>h4u4ZH3bRhN~y2hz%~9mzmIhAB|>C&?3v^n2ny#Cw5bpUam4bT2JdGk8KV6$ARh z>@OJkKp?2iq<)Aimw-&3CFJBhnyfBo7HEmd{bZhW6C+{58PhcDZBqY zaF)nF6gl@c%W!@JsqS1Q8>GcOTrk75vL2lX!4;B`peS|`-6hIhxn@Ac)4Yr8pH<|D0GHPgO z+|3We!>>&Me2t&diueMc_9UQBSX+Gph7hOz7c4i;f{|IDzL{^mYo;aI-Vw3|jo0`F z9lntjgyKT49?Q0ipuB7S-l;?E`Y~bC4i*#i7aIW=ns!|Ga{_120nBxjQ7^)+U{`sZ zSELaD6sE;i|01+xzYet?mBK>LyPx4qbINSELIFu+o)`kYLoobR21P#DIQQ@S`gQEy zB%zC?6T17^_1B9$s_v5>t;#(~(Q@McECd%yIfIyhjDfe#!P9|w^2=C)sQ|(3td&U>f@6Qp; z+(HFhoE-pm+w)JqIdRGsWhr2xk0+9mR}ZxN@DW}hn}6vfgwwp0$f<3R)em(wk;l|s zjt*t-Cd4~`+=aF`g>-aAg@+LUTDpD>w9sVm^5rSx>2sokQm>swD)eE#z&uyn?=aMD z^MJWEi_?+hHiC7hz0}!d`hz-!10&CtYeMN*@_sw+@A`GRzD9#*t9s~RXbuN=6pTr8 zKwxu-y!lPZ$o-1_O)YOQ>U%UQzY!tXx=*C$(w*wJP6Yd>poiOgzBqO!cWu&wN$XoH zz6Iz`e;w2BztR_N?PU6{KVuUwdL*lY*Q`h5&0VTTIroVlK5D0Y?E4Eq37&M(Jl2P% zK_azfoA-!u9--skI|?&}&bo1N?RKn&E!X)R(COJu8+?~JgoI`alS{s8=TXy}rb&>0 zMxAPyq+hcO@SL+}B}nSaT&;a_hj=|-T8i&K?>qnNm{*$iW&$uajs`WMetdj92O-4i zPQy;G1UH=S_oh2s&VX=s*))$B?v*ypmS$S+TOkiKUE8w0`*Ak%Yz`nb_%>8V@MWau4)x}>m*Gd=#11m4zFU&;aARs5<1qztwq3gpO{===xUEj;X-G|&wWMLhr46pljmr(W z)+P@(VTgLGH*>qv7wlklCwuZIhyR1{{DCxsdg!-JR`@UivezQ%jyj764_@R@Uqu+#``U70aS;A>2~eIW z`cMyvde4am3{F)d_f}2LH`i4^Qj}ekrc*& zBhfC*j51?<#W`I+uKze-xWw3LE!WHrTao|$X?-kcXbY4JETxPu0(m1wo`SS~x}C)G z`1l!ZY@56h5bWI1ZTH?{@Ogh>deEWH8TZQR zHO(qOy0`9WZcNsN{4gCU6J-Q=k6i)JlVEinrpx2>uQ#oFf_9V`55tzX7rrVO9-jiI zmdmf_{%$EZjR2DYs4>q4Kc-KHTC3ouem?eA2c74HL^3v}CwB^lAFjOD5npH9YR{b{ z`u0wUAXImVv$+l*M_B{pcPc*RS6ihh_@gQ}D;693G;&HoZKqfm)Fm_{?<>`}`0?^5 zXm}7g%_E-lp`Bk@S+|k;Wfnu1VUxDHGfSSD)czA=^i9zAewp}h*RG*ne`O^1D_|wJF31r)ExCB8eLk%kVdU%RJi=)ug!i# zF=gDI<~EndDMz!HfoLM;?GVe|mrvX-GxENNhS0+4&b2+S1va+e*WNY1Ix16}m0QK# z#8(ffEncYArG;B4`Xl>a&-Zq<$x!I<{O2R@aR>!|?A0Id`Nma%qEzBmU}MP5T8$h$ zBAaR7f7kkrsxxH-qj}(CE{UMSvzrv@x|Eb!X(c^Nwb`vhM2x{sVdZ!t3K}Ewz_~BX z-o%dyMttxNsF$A<;-L)BbPlWE3PlW4`!(9$A)dI%@nl{kHoPK#4YsKZls~OD6KwF7 zG2?55{i4+5xN*FpH)YLVw0O|@!d=t5(ECEtC`X-lpk9?S`ECC~U;#2h8}!7;BSdbA z9Zpw|^jy2cSZeakNOuc2h%Nb;&4HaE;py0DY!vPbp4URAF^k4u%KO(OYx_U)^j z-$VL0f79_k4jkVCJ@Aq1xH)8!AG0+?6~y=fkG0vu-_&45d z^e}P%@$Dx9b6(V!guXO)ET#F8WI491-oA` z)rHEEzDoPIUHoGiY<)Ng(*J1*xHUxEc(S*6(CS^8j2*Va`6SbEMvXGyswM0%{n1#5 zPRa=p5qZVjSXk{m*KYG6?$w+m<8<%{2v&^(vX=6cg#?{F$C z=?;;p|J=RNFvgeFHn}@%$vYi?SrQPqwP`$&Q6p~CZeeWC^l&u|$ozmQ-^`VTqc*Du zU~fWe{7G+FDzP#98q7L!WO<{Q2U^b6HwKP z$GNmeL9Z)a9GgXh$MlUry#bcC@fnn97RxL>LX4RtyZ`aS{_*osI|dP@Ec+rd@CPK8HsIzmUl;eAHjEw708mmD7CiHqY@p$C+@n;w3#(* zhikcfM@8o8Jeuhj*};dM#h!$6tG;)4_|zvvSR~&J`o=QtuMjg#-@~D?ae775wOL)&ZBv+jiIKyT z#pV^^(=1c=51*akWP(LR{Sjo>@6Nq#)YGKw?M=0Y*Ryn= zUIl5&eOM=Y#L&sE#xZOe%R&GgzKLOyReZ^#JIlm_GaH^<`1_Y8O#!ueg)p1CGgyAn zDrY9uf;1uE{V09w{rB&9jyvp-hX@kEU7&V-3m@~wH=1h7fX+~0qy!r3bc<48n61`f zZ6U(|q-&0($s^YY=~T^NY;3R9gdDkWWWkGmoy; zs^#K^+W0*(GPh+mlZg99+)v4-cGY~u8~=mOjD8p-8Ty`)at>39(<|8*pO*YTa&q!&nQ1N~1y(MaHPwe$9oz_y|J{G($S>pSX z>Uwx=rv4`v;J-I~!GB0jhlw8uu&G(+HSF!U)j}QIYXHJ{3(Em46ywh^87;vJeGF-y zoLwzxNaDBn@=2O#xJq}UE>sPW@;CtE=f3wmTHjAY9`b%OhUg~$JLnm}?mvaf)ZV~j zI3P#-@K;!TgY^D1`U>DmHc3`m==LBeJDY0Syk)g%r4IZ!h{qL3UcMXnTEzK` z6F4Dv(Jt|B3(0^tBNW91KW&a@kYJ5vu!+lbUNMB!NGF&*RiH};93 zKpBz6Bq2L3?RIg0M=kUX5yy<_smvX7(j6)i~Kako9BUk^`zZ4}r$$|ZsNtkkOR_u=k_uxP*wKNeq=@{j*sM736q zK-t~ioty&8C@6tEOzno4>tgj;mb^YaXf<*;UBcd@-EZT_`@VOau22FmlL)$e>KKT8 z8S?7u3~9g5jarI~ofyS7E8Je^z5pJ}D5{k{9-{s{H5XyTpGW(jdkIbK6Hd`1!D7^L zfmg8nzg15-9W{Mg)h6IfE?}u!pjuB{8+?Y>%3sH}3zA+x@4l6CkN&syrND;N8<342 zlv}EPeaq{M*0Arl1);KN4geMEgYuCSEC#6sGo9#g^~#dUoW~O%EWQpg0Lgi{RCtoq z^Jd>;jOY2N&sVg93}DGTPHUMKiH$Zio>5g&$P?zzu9IhfFUAj6rU)EQ(H!!_>esyH zfWGwoIj|k7;{bfn|Ax1KApx*#4o25q9hLY~OL>m1j>OVnHflDclc@p|*TSgqK8RF~ zHUu;@<~0fs(Qs$5njHZoHxY=XzF);#b*$Y|$i7peQ--AaaAzKfDsrl4M*-SJjB8d1 z##IjkJqMgFwH5H*o{$o5d4Y=E$_CUEP>(j;#6dX3Y)TzW%-IseUnQqKl`p03*Sm~# zAQ6x!=pRmpt~q4si1@xe+p~Mf`>ou#4(hi)A(l-|J-Ha7G6$HhHGElsFQ(plT-TD0 z1MX1M^dy>AV2ICM$7Oj$#B=lU5kS)L$Ya@2=|?pG7EPi4SY~6hdN?yyRuL$L4L>~< zz?;!TN?z4d>Q3jNB3Pz@iC=0lBV3H2BFnSR5{;qtPfknbyf6Y`1^QP$ByT- zo1!>>#&g7<8`nbvy-)<+jX}LetsM8Yu`l>nG^!j8bt|mnS6xP(Wg+qsg+ca{%!;v} zuH&ypfs)wa?-f8rNd(n~8tINLuzxHUtVjh+?Car3ba)$nYYV{lKZmFDTSRqd%S>@y z8Oer9*$%OTN&QUxM(`7`{0*u|0f#q$=>m^jk?Pv;J#uzX^|;0E`1r=(`c+`PwVCX*8M(hjU|F0fthC>e2HPM1BK-CY zAqCYwa_N7>@?8ytB0Y^c6wgq7W{`Uk&fHy*yK*^ zJP_NFI;ejBo-uw1=#vcFt%zspz+LV82qSM^SBmR*oDX)Vz6vvBQ1kJF^yl)x2|VMr z(r2q!C@;gHe|bUFnXpq%;t?T&2?qfs$5Gy@VKG9JMGcX9hfaFVk)}t8q(8}6>&K27 zAmLSBi35AGH)q#f9l`h_MzzFV<7+0NYZs-UoP6&wMKl!I&(F^S1xam_kMBRm1B8bw(WC}Y`ujWe3j^_$$s2~e~d z|A1D?0>m^gz;4sDXaM94EnY=-qdIRlJ#kWx`*Bd!RP-04%Qu2jUef1ILMSk|o*h@z<5sZ#m zp0kk^FTjS=TKeR7`F-s>BrJN>Ky)6o5QiUV(9Wq{e0#tRkn^mtFad?etz6><|L~ZZ zr?dqU(0iHj3cA8PR3&EX`oNN_-#?Q2Y=ZQ&^e@`ZAeFPuu%7TZf9B() z{D)N;6LZ{#FvW@{^4cDdkk8=|) zeO&jWjnR23D7O+=rkZ#P*cy&L2mi>+Wh!iYjG@zr_rb0BOLIAB@KWIqIOG`TBO=kxw*)ge>MSeg>Bfw2Z%VbAf+xq zBM;}uvNi~iHVai?Yq#WNkgEdfdz*+ji8SBuZ4UNyo| zaNmDH@rGBcBYsK=d|BA~8`I_{6Fd#_c;KEVb3z?k!qe9(sr=#WHqnGQ+x#s*oKQX6 zDu-O`h_Q6NFe|i`2jE*5XoBuw2)u@^&y7khjM7?V#E&h*nzEOz9w>W?H0%yXo~(hA zG+?n?GO-=PQe*PhfAaSGY|geld*7IOOGvSP?a}DmHVnh_X&R>-EKTDW?;15%sl&YbdpvYK0oS5-#zLYiW3n;YHq)fs!FHc&3&oNVa9j6@V-eE<7 z>8h3lb^@H@3Es`wv%x!J2cYmMh;o-;MqHaoha7>`Z+wVME=d{Pi_ggj7au&z74!l_ z$6?)eTS{hcNOQkl3sk}9ts)_2!b!C&?+S~a+_+k2G~eu!p)=Y;OnR7?E7df>|`O?__vJk2hQmD2UwO#cm)A{v(i+v zj6FcFgs)#wAhTbgQpo38KY_R52Y+ZNJ~E0>dOAiBR4*DM4ufZzkNcmjsbX`dDp{gf z(mfT{)YmKAe~CRzIP!4pNMS$7bHVm2Q!MU_Sq~Oe$JQ^6%)fT@f4paUh5G`5zInCe z9T)a0c#xZI8+XyeEu4S5=6}St`}WumUmYhcWWo;5(Z+vW+i99WKAGk(i^YE|96>RW zvJY_MVX4tMaH=8RyfnA2NSO2Rm$rDx&J^tPy|7v`jS|cH%nuz<+yO+A4N(hq4#+rQNtlTJksEm+leTzAar# zE?hgKWDfI>a|?#pKgOZJh&;Kny!{_N{9kLx-`~(D1tKsaw*w3f|2EkEtueyh*sC40 z4wFXxCC2_W-eTW3hO;n%xUoI*{U18@zh>g&*KFWZuboVP=KRB>`mbx+>tMg~|GN#l zKpNHof#@PA0dYN11G=Pca7fZ=#+9m3Q{wXPT93L%jCr_+Dn&iNa#aaZal8}(1|^2m z)qtEYwD2{#&vOlF%-cdZagZHwZlhIlF5uQGJZ|rh#C39Oj>Y|lh1JfWx=bo45mRXk zJ&c8*0V&_U>gUJ2hXtzLV7F@=TK-zX0%XWS=?+@(Z*LVm9wpxXhl$0K+^E2X0+CvH zv4b(0s@knk>t~YzkoH?1Zfe*dtmBDEya0e#*X^_6_xOh2E=VMRWXDnZc-d?R6tHPN zH3N2r8J&o9Kgh6}{Uc<@bDDx|I_mbv=&=HiJZZ3n3H|lcC#S$TKt>bfMgkUJGV!T> z)~h3^Dq&A-hbMjx6;@SwEhPT19LP;aPU`LtmV90z;>?vfnitq_yzWtCSzPxq0`NWj z+9`q3%oYf1Z|#7zCKYVu#u(g3;Xxe;Er6?t4II;0&7d!+I2}#=kAE%A-u^iHHP{Q3 zK6?y3e(L|WC~P!`iEh-EzrRS4K5MIp0=)CF+XkOH{XdQvxe>W(-b?nk17X)BR&0@7 z^KEZd!PFBlTUEerls`$xq?vSaDLZ+5!|wY#8}6Flri0~6Zu=WO!sFdQ-c;6D&FIV9 z5ZK4{(88_sA4e@q90YzGN*OC8@w+sndZ*XtL67^8hGGin%`}kM!S{Xu0i6|{uw*9? z?)NMP6ZZ-M7ln6X7pIHzj@Ug@4+-7SNQHI3ICXp_x)ER?{C>wIDuf*ojQ;szz?!bR za%5j102o^b`Roz*D>68zh^yhs<`O<(e^0uxHQqF!B~CEm z_?;ioEP`v?<{$S}zqt*H+$MR3HN7_R&qj>Ke|D#AeMl8p+H1ssY_tzYW3Hrj+jgvM z(|&B1WUxT93de#W;Iu4UsTVv@nKg;562JWdJI8vjs51tQ?7j(?Pa{T^JwL3nEgG1k z=r5l=4(FKJwY^9!S9Vx)ZhCA(|@lOU<=__+vZJ?4j|2mB7uBgj|qYrR;?SkQ}oys%|4k0hA$&m*>M5@_3k2Nqn`7ca*x0!aP*+l z+13x(YD@m@611^0ut4WB8`+WC_N<{yWAts-*yzO5uU7>;pU4RKpWZzAy?HX}HnOrW z63f%-(VKA;C6(te-dphbTi?+8nyOUZnfA_X5ySrqoqu^F?2Z!&#lY@!*B>GWG4kmT zeV9-PsDZQm_)A%@E*6?p0M_eiJOR~f0ey$cMBQ3X<@j-4Zse|G&39fJ!(AC4MnGYT z(f#<_Y6pOBV-0r06FmxtTN~gR=7nk^pphi=Cn4mHkHMJ-{+#dN>%VK9>Ry%()cIHm zxGWachFU)`k=*GnmtWLBzMqgyyr`2B;W?2E_^lmz1TmH~rv>#Idz z&r+!ZB~f0Td46rmBw|hVU|kznRQ05t_5H3GR4Y(=>hZHZMx7rFRCM)Xn)Lo_N_ELe zK}X-L87=Q)YYk{_W=KwNdFCy{yUWB^ zH81}NwPw8plr+Qzduq^T}k(jYyel6jmjm3JqikfdRREz5HZ2-L?i zoZEjbPWmwNmRr70$DKK15G^f972C1c0h9el*N0bk6!Q=vV)*@Mz-mJ93IUk_P#XB? z;ceLfPbcGbHQ5#I^9^o>kEPWk)nG!YOJsa(qNanwAq|8O_3tFY2;BrnSW@mtp< zaQXxgWYh1EJ6OM7|GD!;K*U@rU}so=ryD3noqEL!BdJ`M`i!BY_$ouc%M{-ad7!@m zZx%I53@ijTQ%;N798^~?cl%-q))HYrTdml;F75kx5-Et-IXz-b1?$n9&}CrelVX+S z#qB26YI~X&y4>mS$f5H;5`r9Dm{KS-=J3u#`|*#N54?f^1y!LOsCp~%N6*hn1OUs? zQmyPNyb!Q;X?Iu?>P^Ak#uC^w2m{(KBpR)Nx|4FYR^|FCzqF66KL*{7@#(l&F< zs$ZOkXc_~pPZJ4)s31!PqxW)CI7BTuI3(W+Apk3eIX4{JmK}j|!h6y5=g>0%q>i(@ z;!e>RXV~D`x84kJ&DTd5kDl}{9qLuOi(J9q<@@=0@57pWE}Q#+az}cm$K)-l#NS_z z0SWj$u;YD{s}6jOK2lxqSE{yS7-zP<~mVZpHFF7m(toxKWCwGGoX^ zAIoq~SW;q?<$*kLuBS2RPB%t=6%paZYT51C1$2R&V%4EyITl~NB9smYY>J{GLaIQ~TU;{qw4{k2EDM zq|aZC#^6cT52psdqHAm04GFgSPE&S0fK+Q1w#meNoKhh+xqV3boL*$W_4lYyV$$sK zs$#mnWA)G4gUJ)EoJDT{GAmPgbzB!2e?+Y^Z|FI56fU{OjYqCAQgm~9;EmPe>xXO8 zE|z`%T$?_vo}tc84`qUR7;3|>T?}as5*^C>mzUJj83@Dr(Oan`_}tBOjDU-atC^=l zyK{cBvvy1F77`eWC07i|n`GU}{HOe%B~=y?YE8F{WzlQcKyuM|C<2V&tc}_I&fs!x`}V%53<=aB-a# z!0|8L>9xSM4s8Y#WrXMv%Kb%Q>O&H49&Q`nPN(Q^zYsb;2kSP;^^|;(`SSDYFIet5 zxGvuzmomNKe^`do$J}hYI?QE1|3M(sGHFUe4jK>ZA0X^s7Y&t8I;!a_(5y%9G6ob; zwgZ0)lQ3&U&`8mdQ^D!5zBV5*weQQ>?A3_S<-7C3MNerpVsqu;9AHk{JqSKAiMVdB zX?Uczrjd zK3@yfdLVkb*}@N&4%F9(F2WTwZnGHTHVCXJ&#%Y9TU0w@9UM+} zXptQK?|RXrY++=<;`HV~gWb1ZTMF_6X|Dj?qv_~nYgUGgYZOP?PmRJbB4=75%lF2xi#>%E>~l3tJVpem(<1hBk%RYyI`kJ)5NHZWo$m%{KD z2&M#b7}p2KgtET72%|Cj;>jq76pU|R%XnhI5tW^wU-4;Fn*5Y31v+h1*UU@l2awPz zoZAV3pnOvrbK3qTna9ep;cT%YQqC#taP9YpuiQz>aWes{BQi{D-a}aP9zmyiu|Db7 zr|VnquQiJ|3-|F&&2&VN(mR}q-V)|@N+UmSe@dl}i~eNmR^}K2G&E-&Y=_Q(q1WKJDsB zud0CM$ss|8!Dtg^h4Jj#xqEYNxqr?f_I_(Uh`MQFE=KnvT_L@YQEo*+Q$kn_nrWU* zej2&#v(r;$?+S0mw?Uz&+r~aRI6Rl*Wetv(EJ81!)IStmw9|Ktv$uk^yUB==FR|0Q zf$9-jA!U&c9Rb-zy?jbOzfd<5p1x)gtmzUhSm#5|i^37FIh=iFZ={DY06kzK!%9Vj zI40)l?mSAsNO9Lyq*6tHcB>gy_--kHNI4jNokz8j63E0)sqvhl3uXJlESZ!}*-LgM ze5cpWHq0hjVuY|CIJNPqre#=Wle0yaHT?jb{^TgKJ*Q!%x+s0L!u{V9)qWgSM=5+Q zJzshH_@7Jd=e9NC5@7ff=urJn)z`(4P|HF2ZXT3C*;c<|_UIIzdyml1?pm*n6ktO* zdET=V`DwUnLzdGvK{&{4tg1nrmw)UHwQ~b;FH2gI)m05z=hTnph)#KaMvsYptxg7k zMQ?h(=Yf;;XNi^xs&^x(F|ex0-2=62TL-t^X1ttDSQ888JjV&D)vvN6N?esvLOTvF zK20BP=A3Ui1ngHnAj4D)dI$2Td*y2%^~XCcagX&KQ_TUA=k*-)wVDr)is?~IA~bhE z7VS43RNzhlPsbzdN4?c9{1`$jb$mQjfIhO9zN-f%;j1&K{-Cw2aJWzvk=>F% zknOiGzj6vNwEkl?wtSz}hcD6ACB~+11bvcTnS5Cytaa;k2XZYQDwAGe)>#_9Y0thD z8wUgolA6uE{!cDIrQKVcl#~=_jok?z1lAd|Xv4xZE{3vNd&3sLxaS=OO;grj|F27b z$NByRq69_bQZS>KOXH{?A8z*N88@sJJq{N27%{9G>y%7t^=T^r*kz{(Td}NpXWV6B zg4^^S@h3F>MEyTfWfTR!&H`J5H0sOUc@{K=uJzUPFSLZec(JWwFjYS+=?AM_B+`?q zlOT_PR)Q}(p#EDPFO&H2n+&F?Otey{-+&ua;|o)qSA^qGq+oRn*j1Xc-E|*#&`wW3 z9=KPcWP*Ic$Y+XD|MFVzz(5-z5KhTPl9ZaR-HZfa{4;u|hk% zT$_@N-GE`bzOJbz%c+@gy69kKiJxnP9W2MMo`A2#B&*=n+9c!UyFZzXhf<&?JDb+1q z2D@zu^)x}@wx01DDyN4cLi0YouSlM9BqUE{3ycNufI#wj;wZ6uZ$$m>b+x)K!*#?e z>~t%j2-P^jf$JY~sKeSbLkn?b~${V%fRK2{ek%l&S8jXO!_QDsK zQ7+tH>vW&0&I=$k8~iU8O!kTUAr&Algr@|9- zEFfUoYAs;We2r7~d?R2Ft>eQ`>{iA=!3)u3>Gq57?J?#7-iqJn;xKz`+#Ti za(SBDP<$-k6){!j`@khmz~kVj-PO^@aY}39q~t2{%a&tB-{>E82|X8IQS!Nvtm3w~ z2Nl&3xGa0~Hy?2RO1J+I8T0XgsAqDB0fk~l^TTCtZcjIzIdB6z6`v+*#+xXC%fX-2 z>2MpcVJ4YUcfD1VE#G~8;%71U#fZcPU#fVKD zWhOX%;-$Wca*-}%fVM>Dg~2)DhpH3K9X|_ZVPajneXWGSTnZ0&UfxO=e6Y9a1t5Fe zhpd-V@{ynRmZHf%C#%5vQRB}U#Dv|5VEvVL8z$NGtc5A}nT@xwr4XSTD=E>W^m~S6L8ai8GQ^g3o!i99gpo-0%MCX$G#6&IzLEK>=x zk_D#S-QPbXquUfw!kTLtNbUnwPz#f3Wci-e5TQ7qQ-E#UWi$)qUdtYv9O1z z#%?u9Q=GLQs|a-96xLsayF66zKJ&5c@=au-S>}6kc-j=+Epi>*=_25|uXTktJ}_`2 zdP=6j!|7e50mHLxKUQ#7FLlAaO+vM0b7YQswvqdb*6i}LymZ!4Dk|<5qw=aXgb;0E z`t^89I*sqDzFsa0R%5sMHDj#@20cX<=8s%v#css_rxbX)V<#<+D5dOSiN@>jF;WUc zJ>mx@(*XvkT8W4l`mP|uYs`v5l-K;>bk0-h%xR=dJCW#pBYMU_Czc1-vzJ812xro2 zS8~s*H|hy`dPFX0gw|uVQLZL>4?F$=N0zn3iG*RQ@scVN-d$=MmbXGYjAu;A=mYeE zdvA={0qY3T!|zYC(6wh^WSq==+)Ugjao`J>cV( zM1g^|!H-NRshQ`j9!Nph=zRHg3HM+J38!P+fq-aw5e^4tNo^U75p4SSfbqqb zK7)b>ECRlVvOC}IJl8X!|Eh@2>$O+unkT%)?P*lp;Qx|V<~&?*tIu%!#9)*5s2X;J z&OIRRufY3d(V2MN{0PzytQaffdvr7qn6B}jn?dOe7zSZs^T9TZy;i4FYUc!SwjS@w zpm)5M9Od}dKGYa-mdUF(YQ-@%2kMQJ`|zJ6HdcBcYX{V9?;B%=B5fJPT^|OKQgre4 zg~r`Wkela2E(cz|qxpTdO1?(waC!*mKi+~N6jZ2$A*GKFAmz= zFaB6A{?H$u)+1#21>t`HwiDXmlrT#1&B{1;ohViVw5yjmH<#y2ztyb|OdJ!DcvRUTb~oqG6X= z4XqH3@H}fMIrvdJSi*rVc#(8b;6+dm%M0j2GE5KwBwUg$)#*% zbe=S1Bv(c@cTdscf#b=(cIuCEW1)@Ooh&~EC_iincG)W{l9!dEK{-Kl?I@Rei@1Nj zhc)2ivl|vT=1sxmnzQ=c@G%EO7yiqk*X&5a0&#`tYv%Qbrtv*4-2%af%0s?@_|R)L z(DU%U-gf}cHx)eO;P`C7mRPJ_Er z^hEY|1i2{>#v+Vpp$F*2&6vOGUZ3`bxZRZc^6pVU?z{H0O?~*!_HVDQ*sstd4wOUW zhAO@q_Yxi^(h0fuh*m!V5XdO$gPGOntCYE~3-mpB(zVkb(WnSl6H;oC-S^UEobs8->7qw-R|xq-U2$Dbm(Vb?wf2bI zMJ9Pd%Bw1u_>>oWG>nFZ80=+z0G>0Zde?^0U)#P{-0yftI8mJyTP3wm|2)K?E-lt0 zZFO1?UDXY`<7oTTWhWhRrde!o-gd_DPVyfXve8k<&AVs?a}#E9lstkp6P^}V@_nIg z!39?mHxAT4##^A~pm^j`LzpeFz5ik$P?emol?|g^jgJ)RK;XPEepx|u?Q1nG9_9+W zZRGFuQ5P`9#$4oZ1v>fwjDMB^QThTGAK~g!98$o=qCpN7f*M{ZuX4i2(powotCvOi zW=<~`2l3)cVD5u6C2nvs!9RcUMxe@F>kX>6g^^URIu_Dc=xE}5YhXswZam$5U`ST5 zLDbQiz|*O$06HC=|KoMXcdcEFR9B5E>Sp{9Dx8@K&hA|AQ7*pC!<<%47?R@+Yf~eN zQx(4Dz+95;=l*#sPkvLz3iW{TsZz^B4TwL)6XNTCC!aaMV{b)b+$v-#`Iz}dJasPc ziDdQ%^tu8uB&-i%_;~Ij$wNbq^yPPhz_^_W?Lm>+=x@S+Ls?k^smQJNt@-`M z>g{w&%u&X^_});vR_Ef6T}K?9A)lwniUZ#gXwT9qtF^wr%>3>?9gf%(b6~*XOvhs7 zh>`mHv}w^$UEgc7x7R2t$`JAA$%exdAK-}urV(AxbJ$LE-M0TNaBwJ`$?;-2o3qn@&i4;1oFZxnEIh zwbGj8)hPezK5kv!>`SN6@n411|9&G&h7(DF`JQ$GV~XH_1*Sf2`^>Bx-uu9r{Mn~mEVY?u^_3!t=6z8?H|7x*aYJBRPWbAy64jcVX0?RwZACPwyw?aSvpg867-OK#vUC5aZRM5xrlyeu%WBNGirx{-kXUy zru+FCb40orGMb<#xMWhh(>T)j&h+qD0=-D3XL{jxX8*4yx+Z~m`)|G^Er!##kE@<6_8PKn!&`y3UYA0Z&+7{j%=qy8|L(NEU+89B zVCXWgaN4`uII*%J_ynn-!quV>X=*PDEHuq%cZi?;(^~YetJy=a6>VHML(TtztpBC3 za~B%)5?beK)6_F`7fSH+-9IdC|J^uQKFWM82R407=&~t(;q1Rv?f>n^VOSjWJ)7MB z_T?z12cV72ZH3MMTLoVh_ao}v%u5BQ#)21XIkqx7jSvP6qEMDSuS5ygIO_fR17y|sbukES`GkeO*f4@lksXayK)SY)=FWnzp|~e| zE>x!a7TgDR1_i%43e0QPqHSylWHQAukpMJ%m`Gd{2pmec z!7aB6XL0}@7}N!4yM)h)>5)44SQ&2f@kqXt0n&9S^wFQYMX4~^D>B2yXYn6w{kb&L zMcQbdW*fMwsTyebB5CdlAY9%|lGVAO+l#q}j!i55f{jfzgRW30V%n00ftw zH>MnSefJ48yc;yUy*B8f6!=&x1-5_coeBFjD7B8TEH5ZuQ0euU&}B5eQXA-#n@ONg zOw-kp zK(uCB1PwQvlzo@=r^iFVxB~jJ|9hDK_b~teGczyL4?%(`Pzjvj8hwD$3UJH@ydF=K`=@ctMseY?bMzH9s!+`M9CtjE>v4Yb$Fr=og{-XLIR^p+0n(sP$0M_~fERT(j5ReH?i&WD zPP|_Rtih>MaB`d_K-<}=8M{9nW9wa@V&~JVuk*n?@(5T`b_3I$j2)fpAeh8S;yCY> z0Ku6ExKiB@fW2IKaYDa;? zSHFGrLQ+r%_H2_u|NXOH_i3d})pkRqN781Jz;7DP>$8cMb2u*}BE0{Y-h`*N$!A}LGBh=6K zkEh@axjfuuk10QqLeUB%j@)Np!MRHRug-J@7F8ss0_ShR7Srb71lZsMI*rtjzp&G! zq$V&zkCr@K2Q<#IEBj#k)N=!v!LsW+vI1tK!OE!Vm9<;Tz`{)gEpq71_JRSZL-wV^ z!J+5LofPtQ5A#55+oC|f4)6m)Db>L|RZWqo?7Clh=}#v;fe7bHMR=;5UAim+=q04Y@D>I7BnNAO<|Gfu zt}J^q#KJy*B%=m#Tqa2LSUcaL^1BxG4O0P1SO1#T;u;m8Jnybv%ChrgxB#>|TQbQ_ zSs!*vpVPIf2XF4C0)^|3=QP{~*3YNx%X>%?98>MVsTacRu@}Ix{tMQN{I=oeA?WWd zED_9E_ z@n`wlsBb0eN7JEnAD7har%{om?A0zL>62vQhIay5Uh??G#zb|w*h>EE-xnL8wFR^T z;iU9qYEexRSG+?1h$t%TfACw4_eW+v{c@{d?%F(aIL&aLdUn>z;fKt7K)tl381iZI zqr-T!kwOP{)qe>R0#+h8UH@Syr5Z0*;DbG2ki`S=X6P_N88bUTI%We1pVvar&-j!( z-o*gsUl&;s?PCF6Zg4D~*JcyJ8Xvmuund@jb`o8kOb!KO?E8%n&md@Q8J#(RkmYUb zhE zgNBP#fMx$k^o#cT89EKdkkuh~XT}Dq$ntl(D%w&$YLIc;rVcP=zY}$#yLVwmW-5N8 zCJ{fLuQy!y>l>Gx0npK%#0bmT0f0if-~JD_?0YNMz+_GNSDx{nf)ka1nFW93p)kfD z?+!GDFax>U#|!nLT|m{fOm2OQHL(Bnqx>zE1~@m%5>W7Lms6m%k;4+dauWuMjSY~y z;MUFLVGSB0sO#(FGBq`T6P*f}eq~CMt}p)Z$3Yqri9}m&uLCBK$ZN&t!pR0tBk^3v zY~IqcUA00iBDSyL&WVJOz|ta7psZ9}-A+W;%MDxtIm11}tYi1cJ9iw)0F#aNy>G9J zhBt^g26q`evSAJb;0VgbSzR(f&6Qa4vAGWNAm|nB6N}zLx0?Mr1Z-x<*Xv3_q1Rq~ zMI(!W@as`F-6=z$Zj4Vu0AQwkILO5~@q5hEXq_Z%>v!w2@Wt&z9nmcW5` z!iR?T(~Uukz@@X>?dzXM){cE-bsM|>=D1OO``s3}6)=^VQ0vo7VuNeK_9;?73Hwz@ zD86X{S3xX^E?S=QTbX4KohES9N`Gnw>^S#z5ZSlr8K0Fb{FK(@fW;~rurc9orB{kl z)&3_J;JBI}z1V9}M5MSBhs`D<{$!JxNdWKr(F>@ElaiZ_SBA?zpfYsTE$_foq4trD zkD0*JwbD09&i^dXE83z^*MCbCxVHmyaw`CCiU9hf;CR7f$XAk_$RhXl=v#;iAK0dF zDIgTu0h{gEA#H#mmPXlukpZ2@xI3;oFompK&##BB&jU;+Pw^&JL; z9``4z?+~mTNO)I8j#o zB~aB0rskNb&1@9<@$MD5c&}PfPkBoGIl!`sm0AK+AzlK8CxM2K<_v$nC{RN$8h&vDybx{ zg^;g>2fqgYv6pap;>#xcIiO;)YA|KSK}giL%Vx(Ao?bbhrN#)jnNNOZ$|(P>h;$vz ze$h*VU6t(>H7AgQ!TeX8w%O)Z(Xp(0PEEmh?wijVwwzXi1LSq~m)PZ*&H*9SURndQ zLMC(iggEW8=&TYeGr#!AV)*2ngxqn+Sn05_bo|X)U+51NX=x*pe9CM-F{Krl^y}Lf zdtPvpfd|-l$$*`GMo;Ijv@J2+T(Uc4N^+`s#CeppBo({x^YF2i8oJ&10C;iUm!B2+ z*A_u1#o8RvPi_t2pMulv64YtC!V@BDmk)3L3;!Im;wT6|UW z34bC90hQ_pKYS2?pS4MkI-N{6t-i#P5@g?Q^>$RydwA|`5Wby zpxzflVG}CI1`Dej0*|eZs0QQJ(rlH}zZW~=WMdOV4EO0NAvQv?Y3&sQB7CTR)wD8f zs5%r~=$7_v5jGGz*EE6`$|bWtshLpC3}-Cichu#JPhK+{bwB+4*R}G0?!BOCKpdyq zn4sLh9mq8RXtb*+hZv3|#ojE_kRzDBb2!0C+j&P4KXqiAP7$-1jWPr@H2#EkDBc{YGHHKz zw1~eu8fBa;?PI-h)s!Ti|cB~S)0iV7R|&_#E&I% ztE#43@w@krT3Eh|9~M>xn2>Cw?9fKzQJyF(tb|A$|NB1Eq2qO}@?JmbLG3aOr0E;l zRH%-|wZ`Z{cR}zN?=S@+-V^RbO$qj9Ksm`QetWsa&J5uurWZwJn)LNwPya&! zbcL7qmh{_m&p(x+BF)KS91h8|XY$hDCxbFC=?8r<(hB_)#u+6;e+(kAo*K@Ztg*WL zkX^g(Mc2^pz!=6h*OY@v&J15{DQ(I*N~~?AT4{7Rb23<>ivdwBQ7nXF4q6;XGYx1r z{Mtw-z2|1f$t}T9Gl?-+-Q7(O4eZ13vweYr>u+-yXT+@|0xNnAwJ#VQI)mWpg4#;r zA|d>nKkmmM2!89bHp8XwN)U^l!K;Byf;VUvVY1?}qS1Fak`IcJU;FY%FAulKhWSMH z89?qbS*$&cDlziO*Nx4~o1su{Sz}1FD?wW`7McU(kqvNsJ8gSlCuRTJq3dK|UiaS{ z&SBy54`W|Ta02^WoJZ~?l!(rV-h*D5fg@!?vl!1wMPR6-e~3-%Z^Htys*n}$r~4T3 zL&zD7whmxZi3q%-Aa?;FoO+D7;;UR(;8Ue}w2$&^`nX6#5`!e?!=fufYI7hfwQxo} z_Pna+gzcnWTsnXnoJl!`Pupws`CJa^8J=0f_L2wrL83RT8FMgHUtO@X>T6%Uac{hy z-E64_`N6n#o~)#PI#p=cAeU@F>oT>zk?mF~I}y{1r(@=@?oi#CO|l^-TDG+zjHiA_ zus;m8n*aKCh@y^@1|-0Q&*hsPhW@s1&l@rbeB!saPopl3%>v59PO{x8&$w(vo1?GC z&&=}Dpxl3*L)ovN$mgN_zqfuKJW(wiQPkt*B>i8AiPBa70N6EP1doqATi!S#|A{hCHs6%xZzOs5Xo=w80 zZiR|@8aP%@A3p6B6avLU8Y)CerTt41kIY|Fb(%=V42NHA2V|Tx^sytrUZl1Y!B)=9 zO#7~bFft(emI9#CHhkKhV5EJg5lmoAo!++!?R#mlmr5U+qn;xpE^%RztoZ7Ilq1;w zc3u)(67v`3Af%e8`EN_r9(iTD%19 zj5$V$jv$Haf2T>{LZyPA@o!xF&Z*e3O{yc`p?#}?mhkhJ+jiit~L6=W3?JXB6 zqpyFRBX%b~oGjGM3Ec_#8BJH$G+KT#y+(nA8PHP`7B+^~EgA zBvvhEFUD}`T-Df$&I)qLWl0~;e8yzR+5hAtdncG5`dI5iJLm|bK>4_=|0m{BNIyUa zygo;Qybjo}L#z3+Sl{~m4YT$5up}?T&^071*fS$|1QA_cFWn0YDSN#)b|{vr>+RWA zUupE!-5D;f$hIC$T>nDG9p|GV^*jC+z^gh}{1JsSK^EcpGTB(as#+BJaTy$QiU?x! zzAy|WjWD59ye3f{?*t`~=)c8*%kU>q>+-LL(}2+`Oh9S?d&-i~1Sf=RH>@`E)c~9T z!FTTO!(?La&ZCQ9lDyv{mJr|LDI!WyArAdEn1fc|No1nXF(DhUhJDO-pq819O~d+vNdT>P8TX>44(u>Yx*|~ z_X*;s?0e=C^EkQ~Wq5q;B0)y;4DVV6)YeddIU?Ih`X7tjqwFF_; zG#5)tTeE!2=djkrh$nNm`9tVi{4aJ@E4(HxtsFt;Ge`V(1jqd(2};SWxBkYV$~E#9 zSzPVA^ur{XGzl1J45&Aee)qJpCc0%H^-ss~?dxO>yXVnPW}HDq>^n&s5rg?FisGyeLq~I{;mHV(C4wPEtgFPyF0Utd-Q5CDVXomjs?r{$%Xg#T900jO!oV zBt(@O_8FYKv%mpqH?qzF+dytW@);JxQ75|-&}>fDrLj==8o@f3Yg-~XCD!J^0XkAB zpzm#G)MH~>q4nE%8hVXa|2+kaJi;1;kyynvTNr?m22q?wt@mlre$)0$9lKoZeH-XL zEtD>iBkP2GNgV~3B?(W|>-IV{%KI1toh9CXefc`}tqT4=0m#f(Jkroju8G}}dK0)i zR$=Y?rWv_UEzEF+h&WH5XyYSswj0pyU1YpEEGVE$kp|%iwSRpHcSx} z8UDZBhT0gsn>#7{hV&N9!EKWsER@f(+k9@{?7koUf)VBbV9C#y0bTftH(F>*)bnd@ z5kg8}J5&Fgg|I@ON0>waaL?oj*>VP5bhw-VYWcb3HKohW_@Bs`j*0YO#XH%?ruq1$ zBHitDEl3zy27yZ$p5ikc)HRk3L^j?FOeajpBkqd{<^a%OQZXl1srol&`nf+8!7!y! zYuR!l9|e@fb=?#^VDRvQ`4deq4u!yPOahCa-9MvJ94EW7s^_Vfx`CHjR+w=0+#(*F z%skbuUyztu3MZ)wOzrQv#I=dl{}9)n(F#St)`45VB&7`}Skk>rNFh`457rcS zp9+x}2~R*D;D7#Mcq{!$&s2~*VcF2J2WKL=2=LnYG$Kim3yba1UI%8z$WS`G-DGYx zLm~IaH&QF6ef~Kf6caNnKM^USO4!qLp=&y16ZqWIG)bQ7%u=$!C+E_Y@HQ`&+hIlQ#Y(byG_o-|@n^Axc=;5A z~JqtZDY9KY(I zBd`5=hK*RXs|)|EqhS=6fAXLA6|i+a%gKxv$1YpVKOg%O18HvCNk%RO{`)(`w&fDX z2=nO@d1Hvj((cwEZ@O%|5!2v%z$k`)KoaKtZiKBSA$e#y#!*@VMPH%4G!J!wf?-D> zutckslfe00nUBo?`FW_5S9ix8DECcVdXanpPXfr<{ps-s&fnXksy0&CcG9ZWR)ONP zB_>X|O?wqF?X)62s7($B_W_}heh$`UGm)BB@hvr;_G>_8=tL9+A-Vzs1W%xWo%lDC z=7keaxa@IY-OG!1$VpdnvHC5L>LmzKmV$9~i6yZ23qt+-<#jw9znpr=F4)P*7TK|CkEici zomI?aVYBC6$~DjN_YsHvlY+$$$jsMyJ(ayf7Vu?Kr$b8pW{?vPg)7R%HJwMjd#aE} zYoM;Ez^(c|-$EN}J*Bz1mr+=0~(E9)G3Pv}ico3;Fp>(MQy5l`tRLpyvOM2Nke zx)!=sFvC~Cmx*@Y1ojKP9h4w{j39GdGrHawC*rj+I-fn-bGt#fVRRIpe}Wg| zx41OQ%riJ6=_+i70ZQ8B5&9>)E0N+e6(G>Uem$X;RLtc77YX(BC34GC%FyB~dci-> z4awwu6BLQFml2~>GKX)ec!?8-W4h!Cad3hZ9pE9lhzzu}LQpdt6hk3us4nD;LkI@? zwo~XPhx}_?LYO)z@l6J&(f$6~=Jl&YaI#}Jewt+p9X1XK7$*ahvY6aJC-pWsT+8A2 z8RB4qXeKf#(I#T>`D^ldJA5AZwuZowiUnk0MqRW~v`}$jy|$@mI<$}HUi!=YX2@le zYPD4D+$7Y&Y$jzVIM+~+vL}reQbS|{O1EEwcvUBhtN9wx8=F5U{U9 z)`9h`L0m#i%bzW}a&=xaYXdzA5^#T zd!@z9PGPp~RgR#xk<&PVYY1#YM+X*F>T@CmVDH#G=LVjcAuZLJO26lPu08Rdm~J2W z#&?7zuiX<=>UTCD_fiikKEvk=d;duuX`ypJhLPd^vCY@%fp8g0&V6nCcqrZFdZ+_^Z7_xRyrN|#CKV|&!9j1 z%EQ5?921{@xxt8lG&cMyEX+Cq8@n4{Tg%~aQ=tr_|D5~9ohQ*GLUFA)|Fc@&Yf4} z%c<(Hh~~v@v#xq%Lx~beqi?apchx__nAgboLhTXI1hDv2U8q^#OpKwtj+^d+6)9XU z7y}D%)4865Ae0Qwf)zVRlzLcnANuHfFw8yE(wd;T&^uSRMWCM2H+?|#>}^rhIBe_% zr@svn6t;PqZ7GFq{NSPV&qsG>FRD}f0h{H0+j{ubCoUjhNjzUxz6%05@`Jb9Qz=)Y7b##UMvW2EBeKNHdO&=7wp z8S51mp8t~AhaL!b!V11F>weeIjebFc@vjyj5DxwEAA5eWX)=mzpe=>+@n13+4JrFj zYU_83D11-B%ecO8e*KtI6A)|UXn?vzo4Kn|x;Ni$%9$IIO zKYHuGgg<5e>!jkd;7TlBA+BU$?3`Dq0^qQsC4io$Mm74sU)Wz^4DjxGO`wCjlKWl# zue=EMFLjIw#vrF>-8)|V@pN!wzPA(}{MUT|Y8m@{`n&(Su)k8E2!s@k$M`|6h8bM{xaX=Ug*Lkt9&$NC)aJjjfOWG{BcdLVP<7x!eD`u)lg4D)|f`MSA-$ zDbiNVzfMl;CoXd}v41xrMR585+JMqC9z}Yw=CBrCAx;*Fh$rK41EfIzQQQ1SI`tpv z)c>On?%)wCRQz=PnProB*pS>?gw3AO~wkHbX0ez_bPflrEg7}?BDZqUV z7cJ1bj2!{N3Kt+1`Ayh#q;RE{x}KOFji`lic{UC!)y4yafXJTts?dIfxM~9M%xT*$ zdOlM2Jf{N`^kQm=&#snZB9c-?3*{-#`h~5NZq5L#pbkhXIz_Iwd4|6|Un?5`!vk*I zHjB=~g>&h@p3S(d5!(H_f1)^3bCu!IXfi1E-0e@Ede7UOl6O9K0QoLmx|Ukr7bQdW zbxRd=d;B4YHhKu&4bYOr19iz5n0WfR!l~bmWwcknl?b}kO}95d(-IFxV|xKr?3798 zya9Zj765w+GQ}>eEy&&&-Fd76D7Q!!K;^GjI;h^HO&orzD)KZMvog$_vWUwTnyd7 zvY8s^N^{+`0V6#wTS_nizL$%pU^E$zCFWx+O`6 zh4;T7aQ7fJR7w>YJl+xJ*b?=+Sb^AqQb%?e(+ozgYCaceRdd8J%p{kGDu{)~5VPM- z0{w?umd(;|Z*xGJCLd@~GrbjdKbxssz1-?ZWG<-c?oGWZ5j-7VQn{gjuyEvzn@-&` zw*K7nlBxa+_m4OKr3IiqPLrP_=b1gBIs;>oWbGSM7NwS>^5SRHJV)SvBxNZMbU9BC zWzNNe9%kMz0iXqIZyc7d!A_xL^k)=m9imf_v>aw{h~&AN1ynU8Vy309bowTDs&R{X+E@EKSi zY*7dyRvRZ-AZAT8wnzh1V3JRryYmmQRy7&zn;!Hb4NwCh)CDjN?gJ$eQ^z@v6qPl@ z8bIZ(K&}BUpu?#7Uboeg$GbB<$LoVVjx+%H#+jXLn@IHOke6Z{T&1;=_)X{^Ci`~a zOsI?2(enU@5GdF7`p~HBtbw9QTxJu+d!@&VPt?tk7l2sZ=2ys^A&FNH^zNGMQ_=5$ zGlIpW+Rz9#!lrctwhahB(e zU*4~kN@~yZCNNXo=BTqJR7c(CG{?#dbbytCK!fMG8m}MD{Hbs_Til$=d0xj36kd|R ztXGQac2<&A;-kq!OKPC4nfP~WLt5lRGfNz3!UQ=+cO>Y-fLVge=$W>Qp>T`!0a)FH zq)_gAt4^x5j+DZYe`N(W=Rua0GyO{EUlEWk+wKij0ci@4t^8`BBh=nGJK3T(0g5FZ zyY+8@6F#Et>14WY3jtg1B1l`n*syojZHybS%c@r&It^D?%J}U!FY8W}tmIKQQ!k=% zt24|EcqpY@rhDX|bD{nKx_hoT-C3a%-o;)&+*$JgbU>y6X0k;@OQq_Od$>YWw22LC z^M1oO20+32##fB}!_|(HcY@H#eSlnJ1|lm2r@3|Fg?5p)OZDmA!&%$#Hz90!F>)S2sAq+aI!&JUV&I?7tu+ zmVH1;_0dJ;ZY?d%ijl1zqOL#&#}Sn!8_ddB(AI|=7j>J;jmkwbS3tk0%(2q0VLXo z7v}!!w9ipB<^l@a&j6LAnk+ZID-a**{b4JVlcJdcsd6k{? z3wTdcgP%c{R6t!-4;7cnijgy0(XG48+RNdkJA8mtpeU_+9lolHLeBFeP0wPRf0kRT z@n|!*6#I^AqH>N6fjl(w;jF%MhLT@u@3k`Btmdf~W>D7biY_7xe?EP@vy>|5`#MXZ zdIDh6YKkksnDWK#$0aWqG(qcB8PUbp(Y-NVh!5`Iq zzFVvWlG*$`<)s^&0nx3DpjucniY1YumYfS3)%frIS>@l4FCUB@p;j!glkQ9 zO4*>4QjcZwDAes>P_oHm9sNAh;)dj;*Xg>ucuZ@!Z<9X3_XR2YRbN=zAO>D|bp*|K z@rIL8;iJpq11p=Y{Ft5A1(!ALNTERU-)B75o;fE@{qffng0?>%h6-NqOM|HR`c$M0 z2FygJPQMH9l$BDpaNTC57((X%9+6^L2BmmDk#UUVR$9N@UnylSv@m2)tYL~2eO&|% z0P3ZyvOSjt2C3a2-^05mjOy~1_VGEcPg@BhCMFaisWM@Yt$>rJUd5ytb4w2o4jKM1 zVDK!}81>DKa(Wzgb)Xv8bWpizEHs+~7E`pt8%f8OhXlQ**}TR5y$J+xuwYF6KKTD$ zYTXH>s+oKHqbPyM`&t@JiV{d;$(-|nG4>c{SGB(hii1B1f9@8mMYF$+>Mx<=-z`|^ zgIZI}pX!f?LRLYhRFusvjMW1Avr~;h1^TE_I1^+iivbvZSbmG3U)#f?QPydjsh=5Y zH*ljne}kXWX9rd=Iatbf4x7-WX|Fp`z9G?=`7cKcgZCkL68~;v4?61Hr&0M{{o^63 zQC$_mMVaG{h6CH%3vua${NhkhNBjA?e#|(GX%Djrnhv3G zTTMfmpP$^a|y;^ zg1b1ft&V%Y5c7MUW*HB_UNn{BX~yyMvWA9>LlT<~fcDpH!k~m^j@sUWjW%)5cp%lV zuq~L-o#N(EOb*D@vOVgyZo*xBfEaQJ7!pRg6FDlHVD1v->Ml>pr_t#I z(}R&+1E&4w{{7mIZ7(a7Kjng<<^@AJsj_KvJVe=Ta~yEE0Wu{l)u!j`C`nau58zZ9 z!xT;$I_U_PkW1AI;x=fg+p9!Y*dbR#%je^TyD;7T%)XfDdj2 z>6#Omr##hH@aNJo%JXQ&-(X(bSK(-Ve+(3HBVWE$&vk(R#PSvJX_7wo{h;nTiItr&EQWy>?%Gk;4B- zdPLVQ@ccT7YlT=i%aMWBb?IQl=D6^BHI&JavH863P^eFsy#aZy+xl4?UZ0tur`I{u zJ>n{V^ha^+!{|kR?QxqYa4u#cqdwP(;Ki#QwYh1}D^}B7{wf9^eJk5AyK~VJr27Yt z@jbxS&0iP7t^UROw?^ciaY>XMC(15exQPL>$oq$q_5w?bp=s{S1xma1yGa6EVW*VicOlR(G;^o!fQ2cVt3e;{r=%E>o44{$DaVX zJwffyw~bF;{p(5S{WLEr^lfc{RC~3TJ89C!0}xBH+k2E?44uAmsIyZ%g)%I5c$N_pbS=TxbUt8Xwk&g;FMa|g`%ndeuL|z6^L_J zKR-{SmamcU$X2jj{7QpDipOsgfXv7sJ$|7-h;=w%HAm7=?(w#7r94maUhl?p()C5s z(hbGBa!b%&a)9_MWS>r?iIU!Xn+6u14xE2zz%hO^cTiQwE9o~X+T97ghCWdI92%y1hl59ZhI>^~In@efU=rs!6}MPZe&P2sq;BzH_K(!5P_J_ESK)Ms!+3oHY3m-&yohl`8xvzX)iP5bT}T8 zQ<(*rv`m!Vhf(p`6q{40*B{yHGL+q&WEV2Na8x{gU3%Qqy6(Vc+*!2@~$59+O*z19FN# zRsr+B>jEUd?+f~GZ_ad?-`j7Ea))s)^|=GomC9ZaL5HD;$H8LT>X5vqfg=73areD? zClYiXTJI$=h#J99ajokR&ruJjWg24*Q!ly;8NHRV5i0uX|1YU_5|4s%yZSNFWEUy9uo$xNF z(TuJHQ+5o%9UQ~?tZoG46C2*B*z5Wa2oIn2C7r206UT$2vR_?5o$~BX5c6R(<-W{VZU8i+9gE zc|z5T9&eCO1nW~qY0Eb{wxj6w4~Z*E(8DO<$)p%Q;H3b66ThQXG>m*kP@t_-YnRKxPnhp@NtgYXARS3tgal|vB4 zgih%By6Nh;gyj;rtjJQf(4?(wal5~_O#4o=!@kD z;yuTczrLc`FW|BmH<_~Ld>!od1oj}UBZCcqX~TwNUEF*K`5>8GIwzWEtw)u|3qk_S${$( zGqzLB#0yflKoQB}VPFkz@ee@P=#BH!19BG93qH@j51;Bm&@UG=Cj z*jdQ)nsh78^VX%h`gGFQ<*N(zsWh~7(}xDTT~Ptfc`LaN-8OtQOSWi~^<$1^r#Xe> z3*96;^hl@AjOUHxFW*M|Gxd`l{@FVKKkdkR4H8vuB(+Fm^2VWX;ERqvZQi3Da{KuV z)!qD+SCdVs=3o%|q4~_^GjfGNK$1@CnAo9{t`SfTHqd;yQLv#s)9kU(vOfXUEQQ0| zZ1tU0JGt9eMUiO}Z%Rltk67@XW$SpTIcJwZL*$8(&xj(?geze+;Kfwjc$p7v%`Ty5|ksYQMCNkPS|FS$p=|S7fN!^t|1m$4{6s zdYd9t4glj?)^v0>+gd%$(ArKTp$GA|Cu3w$YenIgXoZLTN(cch&;WXcyNJ6$QtP5z&XrM}v5%d} z`pSzbk^8_wz8|w2%6Am;2OImk>uQUiA&b;LhOt1>?|Cn|b#L*-XeU{(H!k)1Ar8S{ zk9Bg8XqzdE2t$)~Nf2S&|6Qlg*!1~ut-!yf#Uo)DtR0d!GXw$Qd14Z9nyK=b91R}&Q_g~IrIJA zw~v2*9Ge)SY=e5opi-i%%Ae}fbZh;?hf&=x{M@_<&aF4Mb#23)TeFNLrDts*;(n@e zG*A_Wn`f&LFzNZTEGqk4_4PFTWPH>o(6z1dXgkEQ*E49&r6)Gu@+IkPS3>;9Ox74$ zp!6=|4oWC^QkOd!EL32RKgexVXM#+>|Kvg%9L~wSI1f6k@#ca$)pJ!@?K&t`^27}+ zT8%xyIaY8KV=#lO**f}VxRV|~!&;@ny?aR$HAGLLLEuRRIQI5ukj~Y5R{@lv!O=~f z`4RyJtkXzgm-gu$zyLe$F{^S>yDSePhB{Aq+~Iy_ zuUT&?In&ndpcel1>57vvOZabx7wJJ0j@Ve+w>uhdhX77S8b#%&ojfr(X$>+z)M=2C zH!{65iwT8WrHR$|@{S$jOb>p!&-9V2G-aGFLZQ4rAo(r0S&I*(7?7U5?w*}7!y>>> zF*iNE1o~xmp&t15>h5HS32h_Zjv|xqD@C?eAiP{?A=*`Ext+J&^=d$sCO;WKI4%U5PF47wSb{SOJh4$b5uIUr94c(W zZMvD(69{syK};x&8vQ{Yq2#_nkm#bft_aO$L^>YIdxjtDF?eX}&)?$={3L46P5|>T zVxd93#%VX~D{r&@P$=cCP<0`{e1+;DtpLYe67_+JF(^-5CpYv{-&-PrW7Oyc==z~P zROthUL+dFg;!53zXR@hYkPR2^04XuUy+dOg8*PE?xDcPYTwr4%Pf)FJ;AxnXb zlzx6FQ+_9yn9tw;q1f+w6#?2rk-2lV_J+JXhBO`xTmrAMBrk=?J&&!bup~sInU@k{ zP>l9*N5H41d2U(sJ_hL8WL{*a;iJn+>p7+(?Hehc!6&`WBcNQoWfY+2Z(`&b#Kw^J zj-jQR^Mi3Is$-S(u~FovtvHdBSvU}PP6f-c1$n)y$J;I?V}Gj$foo6L+xK#;f{l1Z z`p+C0vL(C&zq~${^(E&fb$$2TIpQ6HC&W3Sj-XMy!)9ncC)>RLy*G_fujKsX zkBH3?nj<-AbLZIr&ave79m#VNVUw@Nxn>)#i zZSb-vtY19%q^9f-iB+tDndnHv7`|ya;ShLYZ{`TcU_xa|k-A>Ap6>}#&+Yk}Kz2uW zdT*ly&!!#78}_4l4Z9Uj0|w2}2lF&Ay5$t<*FXj58Ep-R`1m773$UYaG{CwKZ}~3r zXcP`0V242K9b! zo;#U9QG_C{Pukv4=5uLUA!_Q?KFzRuKIeU_Z*`88LTORC6JVsuN~0UY+3JlUPkVn7 zqI^(gyksN=ClfL5q@tFS3H=A0HQKc}j<(*4BxH9-R8rzHk-!0PK;1=3`@IpB0s5eV zDwkZ)LGPo5-sK0UKq5A|vhfIhS6E+*HMsL^@7}HSQot+r3^hmqx8_`a8P! zJgVDa)ImdU9_zm(w_{+Ceq#DKsSB+meh*7CA0ez_Cd?GmG;ZuFpA?agz_pF8R=J@Xu$G^fr-nbPzO^Hfb;wPw>q z^|;y6O{!?to$Ac3!;r)@f{XuPC5 zzZ+7*L=)5#z24`~ma?w{=;cAv!u}M@K~_yWgKe^{Lk!Ck+y$C|^*R#^PkLVNG4;Wc zso?dCBc*b@6oQs(-_*40WyWE;YbNi$4uD7SQn?PNZp^*3*1VFDod=EK6st>qzHmyD zKY`k?e*KoNY?lmnX1P_VV;w&^AHH1Wl0;^HEWtzKBZbGJH^)Jy@2qa!RDY$^y`D=> zeSf}yd*>0=*p~-WIuPVPV=~I(;B0hp^<)tN7sSIy@a3ZJqEpAmrRk08Mb_^UcZ0pF zm9MxLmG!n>m^FZhkT!f?`jejIh~0It(1N$R1|k%ZyT*`Fj#sa?nsK#nwv0 zvxJVohKbGKkZmn|Wn{o~3m9psgW_Vg<4m+k%CtBMT!o_BtUZs)rG?zntp>lxg>lL& zuOU>C_EN&MiT&nHdgXOuS8g6v?dH$0eA1mth|mQ`2-@i+v2mgI-q)7HW1R}f)hd4@ zClS0<#?}gMXV7?)=iZ}u?7nQx)3(06BH~l!-s=|Mf5)0Tv?|K2c2%j1)6;1DLI)G4 zE5U^ma8`v$EJ~5sUL0EZ#35NS43$`KJ|L+xI%s20t!q-|e8N0gV!N-s_tIy>>y!mr z7}FBlQQSXD;nFISn^G#YSKM*yAjD{RAlol0h^Fm%5ttK3Ywn^}oUX8FFDry-=tgl#G9Ll9u z$?KiSh+jr9btHS;S}UxDFK-b3P|>a4saO*B96al}VTxpQG7QQ58gC?+p< zr4Q}py?fMHa!|>)@-}oNTG?-4{UKt9o%>d9XL$uMrC(g;2{PJmIJ$~!0ySpLNENQMzQDKD+G{#{ih)5B9UwJ3bs3v~9lgj=i_xAhP?F8d*SGeG| zlszWWMEk1ilNe>7r~wHu5}I{{}jcmsJbV(qcDkPkj& zNHB0Y%c~M=_Qv+^I!r;5F_*uf{`4}Rd^d$kzJH*S7=G4$V=A7j_2zxm!e?<^_lq7c z@g0wpqgS)zDa|qrbftH5BWl(bmSIscsHM+Db6=;P)vd8xRj*W-QdD;V{DEdxWtwp_ zJwd!M<<|1;G|ccScAC^+h_9NDA8$gJeGN_+s)`%1Udq0f(uIE#s|&1*%tMS*FWM^N z1ozayeo4#hcU02sN8z?owA`z3IM5bar$RTyvRr(MMpfmcG4d7T(xv>Q@b0q5aXOXl zM(8Wi;MGde^t>?_7nW>Egwf$xU8N&2t$}%M2%GCoS+~GgwTr29aQ#I+Se|xGAn-Q4 zTc0Qddp?flsctr2%J^#Ww#8u7$eIh2F6N`^ex=lYe`qkjx#Ew%)U-mVfTls3^ zP8r1Y;)bc~n~SofDwNSP;g6!yfavRf{^G74Oedk}z$418o_Z%?oEl&^`#iz9_d&5lzO?aYG7`IwQ<#2Y_42OcUd@TtJ@+f!90yHyHtuVq_kvTuliNurDTEl5u_S{ z0Er@@F_vOeRx@UXYoV=ZaE>KkxCwSTtQ-cfL%Cxtu6FQKSQEG|;^jbPj6zGmIL9;!h7q@eDPBty~P@I>e_7vKg%Cb_lj60Z=vq4OnWj|X>kF zOYzhoe-^Q>YHrJwqf$JEn2?Lq%uixz>~{TK8Iq~fn&Au8YYiU`E(Y#SMhUzz2e+Wh z>o*GKp83SWLtvMJt~E1b2(<-R+)GQ^rB9+ryXZw8-h_cwG?Xu}+?S(%5hd!=c$DIm z_~;#5iPyfLR}xiPs@lgCFVE9scgKM)tzN^sp_x;q9yI#>`CZk;7{-^g73e)E`8o3BVoGP(ctT^>?bh-%408QY#3O}pg)uMNA? z_ws{~tHU)Taegdox2Y#81&ILI@3|tyqa4)B43EL$u8v={S53|Me)1u7Qvu-c_dzB4*aCE0laOILs>nJcHL?#$e(>hMirT%Ey{Sb)v-adrgz?B(cO zF>l929I^5g%(c6a?CqhsDiwR7r#36pI8*i1IIVKE>U2^+WV^9$N+v;^Fsmh%l zFGvDEx?-Nk@}>%D|)d!kWI8Q;rlcoV#R!`_2e^o zf$p^X(Q2732YbBx%$81L)x0lO)&; zL9(x;J3|$09Ju;j(2&P5)(#4Fx>E&Bb`g$CVbq#+*DGtR0NQh-8Iy`Rn9W*G>H1uB zWlU>8%j<@Bc$DAe84a_w%G2HxD6w%Ldl}B88LiM+1)vLc+CTjA(OM@s<$+w!l>4f+ zy-wYy$gS=bVEU|SC!T0IpsEHn9@=2FcG)_vjGcTQJl`X;NyPN2pPh0hAFtkOcOZC1 z#G&4OE^TWa7@(PMX|S^~7Up<49JCTJ7S4&EH{mL3dfsjui>Ih28($4=2c+GmOy8Y9 zA8@&t^{f^bJOv`Bc%7&54eNodfa!o9@@e#{C7KYJvqT*j;b^=R0+Bb=rdn&5= z6Vfo{ngr)kE}n!Q4P>sT*x4+`wmm)y0d1NNi-+ZP>5!?d6(NU{PNw3M z()!FR=T%uhJft+KY$L*tdlZFrmbcuaF9H;kXZGJyVtNi|Gr?}LHu$wy$cTE=Av~F< zgv7kP$Q}7h!YsS{f_0XEqLdOy2Skw42qcZ@oP;$n2f-;PA`4ugj!ZTtx1KN#Lc~Cs;b4c1MH5N2HF0s=XLv zmQTt&7F)Zd_O9RdUY8~iYKf_B|B+U$$dql+KtzP>-%)bi7lT0fsBoYD#jJDIkEjtT zK`tMslM(8KRLIK(Mt*!j-PO+TXB`o9HO?Kvv*pewx$N<`nr!DP*UGf|E(%~*2zAlP zj3!ETQ4r;47i3)P{JuPQHG7uz9w@#Tv%elDkZPTEI@{Uo(G;=#R`KkcQ!fxzo7>_9 z$$nX{{(QoA*F(!&VikG?oSxYjG{Mn6Jv&ai2mW<=&j_TCdTI`Owz#RcI%Ssa-2>JnSKFSQMqYp3pZ3N8>8Iu;jA^@CxlO)1 zEHfy0p#oFt@2?Qcl_@X>EU3zgiq-cwX9_P33>_A~9zict=q^50o+U5O4O^vl_6qJA zvb$ZB&D|b~dR@kfcC1-ys2F%2cHN~^U;xxzrG?wYsg75D0!!b?o;zhe-i%j)a41#9 z>e!|jHOS6+cWtlK>w)^Lv9bKoyn^ScRoYj6uQkhxo?Hc-THS;vy_=zg^OPv)d5C;? zBNMNF)}-T-sTz3Yblq$TUgU7IfBJK@l`ZDbvFNeqc4vplWi0k+!1E*3yZ)0cCH6qk zJWs>B%Q~L@r&Ka7+cKQ9*>=4$qK&CaJ;O7xTVunlKShy@RnvJJRnJsuYc$T|c8pI> ztTf*xe`6C_(=YLL^1Qw(x-7b^KAP36Kb8?k52XPY@)}kUQZn6`nh*YqNfSCk0{>*U z-j>H&xEW5*aIy=g;Ym))$)D>#uSodu~K!WaiMf*?%YMp56IZq`iV!9UV=f zpyLVH`}FnonPLToh3TVhhDaUzj9)*xk0c`@;VFtF$p=M3!Fd6GBy^;H>HP~I&!n$# z1b|~knkN}0yd{bywn9Ovz{Z3uC^v2LGy&!_*v@>oA|h8qexHZ!=yT(kl_ZPq*w zBouT)tg?RRXHXI}*(1a+X&Q0Jg(w*%((UeFlpo#Opt_ILf(Z&Fn?B4?H7xVAd-svr zkn*`fd}_NtB!c*5XJN}{r49_dbu9_c9TABd8N`($cI)~7#a|=Ugb*HroZ*6m@|h@7 zH}GLfI04P?YVYew7jOHBdp>#(G>*(qei6ZQ1Im@X@A-W68I`gAr++~Z_79&1VlA=Z zM6P1sk*Z2~Qu5ggDS1n*Asw2p-5(M~Tq)v#Bdfs*h)2clBcTL80Kws(-rFE)+8P0g zL`I1Z@9$qYjQ`#cxQ~>A)mfN=CVPesJ{Ph2akaqee}4rQ*vCLk8mBKl!|Lhjx{J zcoR|%-u}ZUV=O}S4;R?~g0JyE|5>J=K)?UP9i<^u;vYVZ|1H19Ki^UQ=kE`E{QM-0 zN+RXs;{uN9%5Pv|cuBetiFtJN&O#ZQ&d;*mzrKMV1DkiBW8euOAvWSf+5h3y|Ie!b zf4PIDJK#S3hkMXJe^35*Twwoa1fYMuX<1lVu_+}xn-v$^Y)hnN;CaySH9`?7S)^`+ zMC8M$dp^(cBXw~DW&$74d_qA1$pmlI|9NTGKhIaF>;He5;rzFep1`(@(A<9G&YhS! z79Xzwr;cwl+Ia%!@p9@qL9IT8{c?>8(n^d>?Mgr<*KU4QP(22kja5RlB!^2l-|t#Y zVk3%1$>j&Incgr5c!=5L)4;>@H=j-4{(s*eYxci;FaL~OoqM)<#_#>Nj!!F``zrnI z_tx8M`>X%Py?*~jWb<9SeEq|vI(|<#f7|@&JufK~pB`_;&GO{>mA&-xs*-|l-n2X?M!1C89QIqR5HgM&X4JFqL2EU`==mUkhjXDguK&;abNA~RM926U_| zysCvN3RCwd$U*T|cq5t~sH_*O$Rh7bQ$KVgA<|qI_c0?|1TzxYBiC9W5PNDhu-A^w zK>;1#WD#nS-P;(%*!m(Ut{2#y!eV8E)0X$x-N`Y9OT%UQr>r^Hr9~FivSV`yu+tU- zY_m@Z2X^eR1f0SGfw$J!90IguDHGSlrF*1+;}zJfY|XfjJ+uWhL<2hXo?ZoZRX hEfp-G1JWk=qh3^epVa46)5{D%;OXk;vd$@?2>_bdUKIcU literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/awt96.png b/Dada-LowFi-Preview/awt96.png new file mode 100644 index 0000000000000000000000000000000000000000..25f49f578110c578deaf6b07b27ddc5605cd57a2 GIT binary patch literal 156247 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 Y3%Rlr#Ku%@6$$w4+Gc59jQ5ECFJRJqjQ{`u literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/bHQnJ.png b/Dada-LowFi-Preview/bHQnJ.png new file mode 100644 index 0000000000000000000000000000000000000000..5a59b5dacbfc11a6ef18d3207babc3a265dacd48 GIT binary patch literal 130987 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 z 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 literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/d99Lo.png b/Dada-LowFi-Preview/d99Lo.png new file mode 100644 index 0000000000000000000000000000000000000000..40b86a1ab9d1943342a9c1ddf0281a56bfa2dffc GIT binary patch literal 117301 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

BMGWTSvXA{`y{{!*XvVJ=iyrqRm>7%$&Ye)x;!A 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%0Agb`Kjmml=*VjGv{yxZUf?Q9JQPv@Z%+-gmce@ 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 literal 0 HcmV?d00001 diff --git a/Dada-LowFi-Preview/e4e2Kz.png b/Dada-LowFi-Preview/e4e2Kz.png new file mode 100644 index 0000000000000000000000000000000000000000..769b1476139a3564b47f07dfde305925940ff6ff GIT binary patch literal 139716 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%0AW z14v5rWH$Z&5S;8hmz_BC`L~{@_yBSdjzbfa(k5&@sg=R+`eHh&Ml6u%XPu3O?Ls37 z8W9^Hu9^61JwUoJS0}bHOn`%rAM)Dbo6+OFRHNfs;h3Ds0pH$a$>%JgmJ-)> zz3GYvl#Yi>V94DI0NB3YdzBU?GXoJMQGgigd5haYmDj!Jsi4gn-|H2FRkm?$Dw=?p z>ViOQQkcZMeP%1{(+MSt?Zt^1c~%Y%I6Q{WYa5!XiUoDwo?Q1N4p5azxzRgQU(>gh zKWlBp{c7dRh)U$wifOl#llZJtL;7U$RlP(Qcc~DS(q;M!5E5qNYUIp4B<1)>riW8n zQkJ12-AmlSyZI?ngVUqPwU!e(wrKS^{Ko=0L2ROsl^ZgA%8N->>6(fee>N>fN^T*Y|>tzsPMbc*XexTinn3 zDnu5>{Ma(5%I76ZKjzz!iG;hh{A-dgM{+xA-H5hk-#$5cc9z{SHB9OMWJj@NyNnY+ zEj3!%aF6M7v>UwGGM1cq4rzpu{%YzWKdNgGEX-=l<8X*slaSQ4XU#@G7E{m1oUjNg zmAbt;>)VA;@HroX1cfljPke<>KPw{1XPRr#vyS}~3n@2|C55)$%6>$}Gq58KEXs}= zsPV+b#>dAp%tkP=G;1zDb|7D;i23pUH?2g@?+_}}o)=g?oYV8fqx*LP#NUN)k6&G7 zzYIj6Sz1Dg!TL7WmUZ4*j+l1ZLf2uUx_Bx^bmn~=|0#BRYTk__KFMax^}lbxPE}F< zWB>WhsM%%jT+5$)wKkNI62S`&dxdDV!8fMmyHgi1W64{K%`xut+|l&C&~3|=9*Q90 zG($f(%?GuEd1;y}H55w#bx;x_s05g4k;5g&t}2s?eya6>zCZj{=dCM+R&y^NKyYI@ zV^Zt~GAHY+ccc`pYMTIWW`()))I45$G>xh(Z^0I%Ymw&9#ozpqj4ldM^{9$dt zA2Cxg_|x;d(rp=Bkr-q=znivDy9Wl4$oXp_^IBiub5c`N zJ8XR;$7j*{v40Ux#9ksQ0Uw$_u(PpI0v~jDMVTWVE!*ge8+Dj5G#jg>Jd7%R8Yo+XtVfPo5P2f1Pd^*%+w|yETAJNp&Fs-%U z3YI`xj1UWPvF%vk6BT-B1Awn6N@ppDui;Z+F%8R68WIFeX5L}%C)DY^zeA5TmQM3fnd7g zM0szdiJl00zI_;O=y#$_i5V0f@-f>#05z6YJ?5J+E3(zUrcRVVLD19XydSJ*dHP5) z)L1M*&4R(pRZ4Lg0J``Nsd?#=46qpt>Rm}y8X6~kzKKdDIcP5 zB}-+uHBXspu znp%!zs{w_+;YJVxZ~5gyL>8X zltnpu4J#I-+;ZT#l->O1r$KNrj-)9*s_t^%-b}QV2ZDM*=yWoTFMfmlp2K}(y4SHr zYrfaJpf}0Ee!8_^E2$-;Gy@7z@bIL=9o7>Sld1B(uh={uZ>qOvYbyDtk2TN+hAMOeK@2du=k>9!>3IfGWk+l+HPn^W7ZiP<9gN#KXDEa<< z4yvy3lTYhUDJF^hNRqS9sgUs*JMu06r-xwoB~Q6>0ngJn=Q|T}5GXWQI>)eMNvqZp z1t7PIF0)l@+%j~q$G=D$5?~H_mK#33hKGlb5pZV{eDWb7sK^lV#%!=(?sH!rQ)!X$ zx>s#7KHwvP=DR>L&bCs?q2R41D3MNlZm32%Dumo^{MAxLd51Pij?3di(~QEHNiMg2 zHUf(wMIpbl9iX$aDrw8TINS)&p7t(Yb;$2gs;48s(}|B-tlN{|^vH?6{gTUFvVFa? zr40V%xc1IiJ1-)V&nseeOchQ1avwv(=ra_nvhG|{LQH0$O0a2h$5&*Eq=XxVC;y99 znxJoov8S<2o=2G;Nf@ue6)*ikb;f0(y_cpH0ZpOOu8jyFt#Wzj#7g()R+j@~pE`;d zmKG$C;_^$Ah0^pj za%|3eVm}luPB$V4>^p+BSSs}g;)^JBY%AT$MH{!@*YHd6 z(EBfJlzd)jTC4I_LSD~On2UP>QcYAY5zj%5_FFXelrP+qvpijNH?S5QZCIA^EBjWe zsi!Veih!#VkEJpY-6DR7hJsEwdZiA*!R=J9Bn&~qLWffs4`X1h{rMPTbHp(IQ~0K| z7nSZHFt+?+gKr;0-1NH4ajjJ)AM6v>bMSB$O?G+c*ps!mnJ8B1cG?aSD?tz%rv;LU zbyXvno*kT+a{s|b0ba~gq-Kie{$)Va(Vf_4T2;|3?0y_wAuy)0$WtunL%JZHULuiZ z1#t)=tI5>yN8Db&$$6qF*iMyGohO({>3r~kIZ1bUb4?UI5?mGo9AY}=gDAMO}kGeso&{?5Mq~~%k zRIdWZBfTq#&b^XWiSY!NaZjvfNk^Zm5U`$-U;g6c<@PV1UjHOSQX={8I3`voUqqTo zw)Q7a{<3_~zU*AtMfz+rytBFYY7OVo?p>`ZD=D< zzx^IPzo7B}a&CA{P0jVObR+=((MTkc3GUxr_Q!^Yzn?l)ZCl;k3<5YKnDYAl4H~ee z*v!A(D`Kcj)!W$sT1jn;2D-5&05W%^5cEK{Z~ci2NHA0q8M%6sdERJ5Q|PNSe6Xv_ z_J0!kY~P+5Ip1)k)y8&sc-q^t;W&Jmk1K^HZvH*%Gr+XMdgWj$ro|jqtE;Oa5fQRH z$W^WxQSB;?59dUHv`&&NNJ@2O$v1COSzY@SvlaYoVN9e2>Lu7`e0Z1Pj<9q^I;xH(Dsvf*i)C z`Cg|0dnQ2;WF-}nKU!zK2PyJQpANz_W4-1mNB%hg|JuT1b>Xe%#bgJ132kQ7_zj;2 zIGnqS2Z0<{F%)0SSBDD2da zviw^cSRp_MKulwnjIoQja>h;_BjVpm3X}0;6AQ^dt6p+n4hC^Bb{&RcdphM@%s*Pk zvF}VKQw?VUDwPdU0PKz~7#S0=S3?!AjauFuGvP4NXoOMC-j{-YoL)t1p5+3!ix7IH z^iHqCC+-F`>|Rgsgu$Wr6;d@ z&ru?R2`&^If8zsF4^`{XBp5~{3BX zF!I1aX-nJ5sK$etDR2XO%UOFj0z}Z8M!iNxboS{Qq25fl{mWKo>$CL1hoc=7+qn#% zYufpQB3DyEH582v8KM&YI6}e-wGKlkk@fEu#n-n^K2TB$C798AU}kv|uYE=z!d~Hs z4w^Xapv}@GB>>&SuCP*j72$TPDUv#$myG}$EnJN$Kt1~}PAvs7Q&-sf1OJ_cA~uvgttE|+xBoAM{o zcW+~rCEl4FpD2Fhi4L}vp!k>|978U!VD}A&x`#n7TXZcMJ_3pT%jZG&kw(6ibrj7D za`j8lK@mpoE}WV7=GVlHjV?J7F>#6)Nx=aHl%{+3pj{&~6!H9Jv0LYS8UM8N(y^4o z&!Kp&DwTd^yN2W4@z)-is9b;kujk`~H`!BFn8-mBbhmQ;#ShO$bL|*MbE=YNOVHb` zaSBQ6pd)OEJr#&Ib)5v}t5#AC**T!@DfbvYoD8rL*bZh8;YRE)KSie*N(9x$QAvu& z1|ngRMJuPPnGQth1FUjX6{#c>J)kKub)#3saoYcda*@^sMERPa`(XyExcOLGBtSHm zVw=xa6Ie%KTeY{h7ptrxa;qn!Ptf(aYTMLDy=G(_WI;|9@#m?xokjJ3dUkzxw|dm^ zq!Rz4<5(O54GJnChW_IJcHaD|F*5?^&-O2U@sploJZn99$Xl}vU1}#z%sP&?yzINW zBtMDSuUFdA{I~hH;@!yJ0obfNiB2_Af@8|*yBs}a9 zF6SfIW~Y)8%fdkn7j?L*fGj*}C#kXzhAj(oHBG4o5CRr@#N>~%veIVq!}$sUoFp3Z z!uf-0H%>lt+>=RDQL7ic#*>1ibG}R>GqU=1RFd@*Wu7c?(p_NFCTU+-f2KnVwxskR!7PD6)vG>ptV zZu%zFW=1a}H}G8GCBLn9Ztg+Nk#su~OKiJv2?xpRIgT{wB}Zw!-aOAEY**q|IiFoB zP4N86K)`>h;kA<~G>x&{YJ%8yezT(9Y7US>s$rQ~vldWEqQ&L=8=t3uxuH#8bea)B zs+^!iJT8PJbw6|xu4fZP-&M~HQZOS6GNAz9m_{QB6E#i8MIUs~E!Sy_A~GxsEK#%Q zoSKsQOfB+oj+VrtUBP2zk8QliXSRfcBJOpuTN!i_2P$oU@?$$(XbMkaO*cAS-$6!4 z?@V}w_w{{O^sid0@TomeBGYu#*>GYh6LRd@)zy_WFsw%a(T>e!(%)d6cO5OfYo&=D zch2|rP0|-Xp_eU-qv+zM0UNG1LJ{O4Is+XEb*u)sVNPb{NT5@CL64Yd(r@pU7&m0R zJ>d`_I_dLr3;A_iQ>vn zl=n!~o1kwUPl`24S;va6J7oP10~&NT4iWP-oSQ=8AxqQ@{{HL(qGm%NNcS=?w{zppLCD5pE) zy{{6G6uO{Ns3=J<1+{|t-z44ZF=HhfEv{$PPN@?&3_icu-BHQSJ5aPKc^UIj1r}qv zO*c`92(oV{2&|-MND3(AL5{U%E@_VQ1_wn>q-oSSoE451;T8ys4hgd1^2u@}jX&76 z27ht6c8p~(#Uy{_s!*a0lt-WQlNT_J35lRY>uHvE5Gimx7VRl$q^F0YGQx^Giicz& zBo+r&4Ne0P%irNuOs2B0kYlgf0K(nztWfb`X#}a^H{I{5YiTTkZmsk!X#pi0G2q-A zA!(l4RqVCw3MmOBqZRnSYd^vy zqcFP6`BI)+wZ7WepmS4-bi2L^v6|jQ{s~i9UzI`Okl#X%MOuG+98nnC0WHr&zr@h! z3aigt|7O7ShL8na&G$X-<}d40{RY!eD(>5X?;X(MuqYI~)+t8T@nvePk>QsW{5)n%-xk7o0 znCs=#`jBBMedpPcma(jy^H-qV=k&V zAX5tkbelQg*lufG)lXcmyDr@BA$9Z+w&^IfO{rKa$vHsKSmk!`;{I~}XnVRm9EP0w z#tL>j`w`9;I3`8#W;#klDa)dqf*}xF`YDai{0=zg2{1RPGt#+V<+<($C=X%I`g^wV zm>!-zN0V=LBGI%eKTxshIjATVji~K%MMSxrTAN&tDD!Z!B;Gdw2jbEf2OfMQ@u+O3 z;B>x%@CxMU0tNLue5E<^eH8g+&EhOl!^H(&*xg_?UyB7Dj|X7xd?dduiH6a}uH;Yc zJuEpw>nJ;d?IK}b&~2Ov7FO`>^BS)@)g_q7|I`8m z&t2-;4-}&e*1dgO`xd<0L7YQhKG(#oCA6AVYZb3zweeZTpAGVcix;47R2_|h(?!^C zRs9$xb_TD*$3LaHP@wJDVgS(R0)j+vl@I|aZg6DcN$1YFvPD}eB!L-|{P!1GZZq(k z@*!m%u6)340V>5YH%*23&_SNsqS^UYvPj1@jH5($bqr`}`URC_R?N|YN`IPsYtmfR zpc+0qH*)9l6GCZdu|m2_M5W%URhiF^ZXpsUg`hXD?Z(m(i!q(b=fHmSS6P*nz}Ll@ zN&019s*=JpaUWrFoyR9s4s*z;j>8@dX33zFCFI4jmQVN0zqHKW>)=qI?G7T*p?n3j zMVf1$hJ(w|o|)+D}WDD62XhULFeieA5xO7r1J*ke-q1r|N++Y+|CR*mqok9FpAo`d*)Ot`z+=!e^V zzE*z#iUAJC;k8XccTUALf*2E)c}bS!V2A%yf~`_%B;O&&AYl|dN!*I%0|}`X$)(`C z3`IY1+Q{Z?8c=e~k1zQ#^k28BzuS*Fin)8@L9b-{qV>k!$b<3Z);05Xi3JOF_-Y-X zMmhZSe*5Ig6x1IftMpWj^n3&WPtDxESwISg#ZV_U7=nXieSM>u)(cx*FtcQtNJ^uU zd?z}T&MQ1?1e-X673pHfWg+$1sr*rp3{v$@s&!*}8n;Wm_Fsz;K0RA#!kZLCA75>? zibpbyvzwq##KBD(B&(xw!PA(8KAvV%56_C)VN?l37bN{olx^Pk(yV!zB}3 zW`*Wg+!JtPU`@r0 zNgOp!%i^(_iU3|QE4AuM;E@jDQHo%(_hPrs{0&gU>Qjt56OURc9}LNu$IrQUwv%Iq zYctgS3`&y>#-8V8dXKLqR4cuDdSiF0-Z`3TV7*GF_Io2AO%P3KOY7|SOw z9rGg~8vvq)kY8OLHffN9 zBbRUG?C(u+f7B=|){K8ChYgAkI9l9`l1)5k@*T?*sKPUZ5l@$&bq?Wuu)NAL((@}Z zxb`XI6_;?m_7F|*a?*uIN5RXNp>jAZW4^tNR-YnD=ONw1i;0rm$xqtFnxOB|9cl$m zg`ZgKJxHOoDHlcz2P;5X$sF2=gyv9xe>&{co<>Pjl zeDG%5o_s3{`8bF-bGAuJPy2%(_X-e023=5ugaxGu>hk;5(QkZY*6@1&{eyb~d*y&Y zIiApZ^z!Kj#jmyza&GHh7)m{ms2}B|TX<9w>(cee%;o$FKg0c|-(pu#y~i8S&y9>^ zl;9gnvGPzAg;~inzxh*_kK(>*SQRQ^Xw7$^`_s=z@IRUD%b@tv{(|XA+4JltsBaG5 z&>#3DCQxv6BwJ5L)HN1X+wc5Jz5%ltI6&e*$P9dSQI(4i3*xpJ9pRQs94{5Qlu1_~ zrll2)l(Q-y5^ATnac!71Z9(OkDVK>H~fioCOE zO7SvvU&VOV9cagP+0t4zCIetQVd9uecKs|uhH&Zd7^kO5%AEu; z4D$M(Z5~zJlvtRxHE{zNsOBCwN6}uR!S$UD6P!6}C@^RwJKuQ*27BebTVmMoOQ!Ei z7mS^#)kZhn=C|Uqh!MP@iSHh%#`ThoUp@nVVuQiSKi7-JdU8b&wBxAcME2h#&bBBS z{2SI~!!g3yDqk>Y^uPr{_ws2xVIiw6MPz>fs7i+Nk6(fFO<2E%1&L4J7YQ;VTw!~7>13-(kSXd2 zSa>R*TxE8_qD6Jafditu``7K+x2f`OY(0GGJOB;0ME$~0=|UCDLY6XGP|s>kRShTZ zr#mg6geBa1WIP4~VgPJoi?Ys7&p%ja4CE`sc~4-2WvK`JHUocVORZ!-e2BThI3?nM zDpyAW=NwC93O2B^q0B9h4w_w&(@CrslJaFn>9`*TGfcR)r~*M41?9q4Aw6;wmW2bhXGB6D8IllrI64%{e}ZLiHaUgVj?m4w8w7 z2Qzqamj;hy`(%UOi%y?h2F-r8Iz@<9x2MCvwte`ZBf$IuE84xhiRb^RZ2VNlNQFf@ z{Pk3IkqYZvc7j4D9;LNl`G2tY-tk!f-~V`ojOelx;j%*{WUtFCAXMmN zR#u@9*@f)P%mx`DGli_|`8!Xq*ZcK;z2CRb>+}8He!qXeerBJ}3=Q?vqWM8JR zmuJz}!)O%xR8_~Oqt{lcF270p@@F8C<$g50nzE+&{=)qEo6Wfv7RGwN8hePT*FvKR z4)zB~9c##6F?zeTwlX)<6%j}O3_}5LQ^C+O-TVyxbl!<%3G)#wq4r9X(tqQAe@I+V zIq?O;%lQlgSq*0Jlh;+*LiPXooA6u62_Zn=Wp*Yf7R5j{Bi0*L|8qk0(E||bucz|g ztM&W?kx^0;A?N0Hc0Y3GfamwP348MX*KznO6d`*TiYLVx74i0V{scC-=sx=-&VRGq z{$ltJ$ub+=iy1I+Od#d*B&JeHX&8PjlVhVufVMH6y7Eu2IEJsq9>XW6@^Yg+7;?e> ztc>;72>yEdpMT#5)vcUs1U6-H@{tLf{WA;GsRn5CZS=0EsDPvWzm!apw&l& zxSLDga!&l`G5C+&7u937>Pd}e!a9XAoiJf{7|X*iFMZj^2&ca<+}Z1I&;PF<^PuEZ zoVQ@19F8FDX$|@x*N<4V2$p5G_L5{NFAG#HiZJA2VAaE8%aC`HvrEzkpc82^Y~v z4~IcXG0~4LP0&`P$6Y%8vk3s&2e8mw$ov!n8L2dAY)ucefw0~OMr-8@uFG>;) zRrHdc9s4bRBfDpzoWPeNY>Y|qHl$NV?tY>d5D1gmSh?Jg*>XEqOL0k-*x?DT4J9nd zC6U7aWrtbu;JI{C9Z(j-wQVbGqV;!*@MjVl8o>&WouL#2C1-fw-}QCiglINtZof`vbik{o?%^eYnBvLC%`PL`T(j;@lPqi6-%=9`&a4Jk;z(EsX zYTZAveXBhu7C=kWnXo%G0De?@_XExI)FlG+dEomcn|A)~0T@S=OM8qQYSqU5AQ2CHIs?@aVgig__5bMCtK=Le>Yj54=^7)N(&xEFk z*hl)UTk|ItN?usxw?Tc%buJ)QQs18s&+sQ}T(cSD(lvIx2O^cP{*x+_6hlPASNaQm1dUgw zZ&mXg8RX%q#CjI!QHQ(E8VcBEi3Qp+ovO%ueB z87HT5sU}zQpX$_HWB!U92$%1XJgt5|A1*6TU^%w^bGU?TC?I~Yr!5=`(gxJ28%59B z+BNe?FkogXUo9XLDXf+~7=5cIL~v>~s6ym|pOR=eb+mlOSRHtB#QgOXb~U*WNZvK` zxBwYc@1~1tR(@?xSbt(8=zc;l4!mTjSNUCcd_|*~?tYB(RBLitlDZY)aY_o_kVMr* ztJ0`f8FocEcdO66%K0VuvqXH#`^zGuPt<44`JuNo=YH`E&nJ&j4&Qw>s{5vJYU;4n znS@HEx?#NW{&mBlvCZDc{sW(PYXz$|&3<%^u>ii&ekA|d@HadWws7dw(^Q(4Vf z_tv6JLOso;g-6X)v&M5$j!|kd98v?x$!`xTLCQ&A;Um$tYd51J=FLxmoisEhzRC+y zwE!7zY(6F?>1RPsPUHC0M_wd!j+Cjmf1mDx&9A0TZCmYm7S-e?hbu;=x)BTuo zMy`RZz%npUoxkujO(8XDW02ab9>)i}MReMxzwlosbTzOsdJfFh$QxY8wA(YZch>;lPMFoa(>g2^` zW*8nX$I%$lX{fXw}h>2Y8O}_$j!Q z+D-o$g3bRO0&K+pT?IrO|NpOo4sZ)>M#TWqc*N^;c;FY3R_9CCKW`>*tD%kT>j@FW zPGjalthOt<5p9TwM*JzFozzp#Afi2BM>p#i;zdJ|=JqRph^W|~BKjXN2{SdE2CbkL zMMTF+QWS4?a={_Gi1{13&E0L2w{GM>Ose$RWW18VYg5t#fz*7^S_J9NuvNg()X`iLWVuQUVzIV$??k>oWRGkf+B?gB?Dl==;dFF~b6_?W~ zMn>-l3sch(r)?Lwx4k>J$_7u1Fi0evisv>d-#uee_n7_wCl=CdM;6^Hnen-n&51>Q zP!tptk%55?LMEx9p@fFjcd1*#sYM**rJEojx2bZp%&Pb4t;X5q<%#1z)}7lQaYh^M zR(r2=`$84hS!*cXG5#JF6!fI_f2^9zWHV9M6XCNaz5--HL2{LzE4Uo$4AG}#7Q#v` zGIWUt3XPiL*dspX>+*a0?A~e!^p8=O{hYvWf{;vZ)Vi%q28cROW~<5Z^J@s*8El`N zT@Cut$={Y~*4C4&^?c6E7zaVXB%y@xSuDh%FWnhl6NIwQ;aP)96ByqgG`^$kQ6CO{fov$O`|5BSA=zL>|Gy5yc6ZftA=hTF(m*bp8a@5n_ zrHXyaypdO$Yfw?R;?B>{KizB6klr)_64OA*i)dZLwYA5>b0G`A-J#Y8%TBJJ6|^f7SizDZnV+1$}murS62Ht z5#uE?w~W3juRP%q^0cK^*5>2n%@L`}>Lw;a6vM%+`k;jfuH!d{OYav&X4k0hDhK@D z5D@$};!`oXGNqiWnMLF9tuFYr-2mRWL5V4QmbLTRkK0g%nbxTKZb&FC%>QC%hN!b? zU*7doC&(5;epUtd-By|HNo!#WxcVS*0B>q06$+Z>`uRfbGpFhH+wI=4y%d%H`{HAD ze)pkFcGjIC7rp8YlRLxZ9}Cn@MPG?gO`;RDY>qm6nAa&g)9E9+=lTvO?if)L<%hQ$ zeVq?6p~fz}%D6fdgd`Z1TlXFu-acGub6fuiFMO9K;qmd6fk*R0`cqK9Q0X2X5wl2a zhO}R}dYU+^+&wJ!QqoY4tm7<-RSXCqgD!Q0N{$I_=u0Ve z57K{e0krea2((2rF+u)-=90`|O20vc1GlD_!${@auG`N~S@vHyIw%}E6V&GR^)nUD zt@8=9kt?mv@`qxHC||n7olF_Ik@tyND?4lr7QSULTJ^G#9Lv7-jHf+*p5uiE zkHsFJZQrTog<66Vv)z}yT-YBzQF`Hcsj-1l!7-n6YD=6Ze9gO(=;FDxAr(v+m)XO$ z^EkPv`Dm5RX96MX4}`%ahmnw^$R*G$)Dm@;UgppcZ>sfSkRC$>Cn34s&N@9dk>4b@ zK*V9B0rp~Lcj;H`C42FaIzLJhny&jP2FC1_hIPIID(LB-pE>nP&B-&B+~po~#jcDu z9*4Xw;@`8qicsg!88iqnx@RkEBI)w6z%Q?JjP1G84LB;=^?=^Xau#AFJ zEcV|&v$a*D@b%3vYx2n-r=;4HaW64Hbr-bdL7R8wUI{(@lk%cfU5@wRWH1TYf<_37 zB0r1wn#WvOUmnxcOlPj%+H9{3!|i>7-S}Ijeg-;b#wC12v}7HhvMql#IfZQ)S5=2o zcj~@&UNXKDzd78GH&`s4NTJ+(u|;U6Gk}`(%;Mqg-7Z;Nw(%Ogg>zWKnZy01b9KHe zy8+kEd96N9lJsJMdo1h`rv|rfK4=u8i}i%)Dwi3pcdfaatmBKuhQFrvbJC@J@|H+1 z^Wmn+1W3!)QLtZOvFb5QlJa@+Hi;6A!NZfJ;EDq%k`#hFCsm~O`(_t2V^lu4u+})6vSv9 z=}2C6Ty@!obc_mEBr$B4waRUNSF`L&4d@Y|v(-}Gpeh12GZi!|W{vCpgG~bV&w@-V z!;De<#<=W-?e>x-2iC*oRnA{@~b{2POM+*ye~$3dK?WTt3CfWje~%MCF{Gl;t0qS0%kDjx$ngaHE@Vk(WC8 zYpNvM4Nx%Ke6ou1WOkoBdu^tLh0U@{MASu;l!kwYuD;@HIF`snmRBRYhLBw~kqYyj z*F!Vw#W&Kq^C@LKo4UK377#i=_pyB2p+7ylCEy^ESAQupX%}Dn6eUs2F(N$024f7^ z0R*RrO9#@W_oJCPTgF>EoQ8_Meka7poJjEB{RJ6Y7xneoB3_pm>QOnOK%3pby>hFt zMxoh?%~2Th3D$_$aM4ff@1DBC zmUr{DxYqM4_$8RwyQDN|f{#G`PkH9x%yhj#&zTr4^MuV`1DEX>oK!G46hQ$*2C=** zL<5zY6ly)5mG(t3GRBwOYBY>wlpJFsZvYg5?U%}dSSKW>j>RaT#QcMaDdT$TlAsM>wLG3P#>N+8}nHspw^qNf)} z9dYd=5+nqy?m7$IDL<8R>AUO(L&b_}zoP)ACrvFpS?~&XqHdXGg6z}_ALe%|Z9OTm z;_IwamD3W=A=EMls?&F?pEXBOoy(Z~(p`Cl>*pu?`z`_OjYGAq+6}Y46Damo?ZA z)TVYEpP^_`0jAmPevXx+`^3i>$Xm-&I8}9lwL}t@_}2F``L45t+|MD=|f zyU5rl*G%Ogd3GF%;=sT!-MO!@G2~4zneE<4~%Nq_5XUBcYpj~jhmx=$-V-8H+7&RPQlbig+fI4d1tz2 zfboU~PL1c{y*c6NTiv!IeT6e4UsqPlJiF4hagU#P)!Zj@ zMNsc8e($NWxIjV5;zX3`EnbHL^EA%+hTBa9Jc&BOc3*UyC1Dk#Kn!wkP5bS5a(ncb z_AHFm<_u9iWDsMZyJ#-EJ}2BidthYte#d=vn#gm1m^Pm0Fq2Q{Ou^NYb|x#RSaJ6= zWqNlXC^eHNF3h(G>p(h1yhH&cY;fgMmFV@PeN}t)j8!Qz5E=^Pi)LPBlf~^(l${^G z&QDE@_I_W&K%{q>nU@e9%_Q@#A!xrJor-r=Fq=~&{np3K;2)oJ>Q~0QDbNaJt6<@U zDTGlRUeJ4s=SQiNChlGmCwg@G3gY;Xpdl*Smapp{;vR&rDxy~f_xSO{z26ls!jL6S z?4D8t6%^oUi7tvCDjWmYJ)0wFP5RkI4ipHtW9@iLk_6ZL83spGYdDFXyjQ@5LCTST zcuV&>9`z|l*Pe>snQU>9_CuDBRQXaRUC62DEzj=%yq4Y0PQ~w~Ue!pksrxx6;htW! zkX;@9;jW3BS+Se$z-E#}o6*et2OGgJueu-lY)+2%{K~jsK6sHqDvsc=u;>2O9;Q{4Aznjv|FkGn4j5p9O1cRAK1L{wUM#dM_^Q@+=0I?P-{- z3K4p;^d>eKY{=XsS5QP=Dk69H6iwtkkeJMB9X8Ac* zTTMvI$3VfkQQb$$qr)p?_XR^~Wyyj=;lzu3b0FPYSq-hgFP6ZH!$X8+N3-QV^VcB! z*`LoJ1tbvPK9lOZYG(<_7$vW6Q=YwO&O9=*`qZ?gL*`_Hw9_pa>eH1tqWUA9K*s(FO*iSQ1XNU_(Wnx3ySCs(w?7dklqo zxsI;vRgrjm7HYFR2&FFk_-(S@UO)EY%%fvLT^5q2+s+dh3X2)?bCaCD+Z5_ytgK4= zuXv@6>%;HSO7rRMS>3Mjp(Qe^wwGO{^2d3gk*X4g|2sTSkL}RDH#|IE9Rqc{9_|Ez z{&hXlzszjd7}yt{L|qCuN$<7ObMGP|B1oWyz(a7rpznhWx#{|x_LE?g^R}(Z3p?dQ z0ZjA$>&hB#?U#|mQ$9XE`6SW+8ECg$e{uCWZ!Bam<{t3z75n*p%avi0xT|2a_C8G_ zdZ&6#UxA*3f*EI)^{M->yu_2isM=}0D7*qTR01}JNgvzJV%>PptAk3{w|BN9-e9{m z(SQlgO$!{>zKrYHDnW1vZ$X)4)H8*>FP$uTs)_0y(YX;3JfI|AIOLvWlGYm4p_8~< zzDoOqU6qC>tPF9OK)K|g;B;G3e{PCVPZfQjks&Q_%AL%yGBHvgV6yJt`9wJ7tr{l{ z%cRb=5&ZTS+74geKNPRB=X}qM&A$9drtEl<{x= zcKKty{`9?PJ$KfKz@(q*ipFbG2WbgH)a-d|jQokulSS;oA9KFdi+ZsHQ}D9g@>)CX z7@vgJVJ195#dB9KjGdxEQt4#<@i$*9CeIj|v8!&#tSA56WO*5?-BwI~I6m3ZJXpcW zT4<~m_{pLt_vufTnoxRPXHil2D|9oqs*?{@YFT1NYRFH`_HwoqrX`PSu26;3{3iL{ z^QWEw)ylGK<1FkBHCrs4$%S7=ymdRxeub*6Ff5`U>=7P2(nCx;fIAcOyEm&^XcGG= z>aWKc4qgVP7L<9lD^Q*6dLP`T9KCno$Q^Z(QBsx&U8)}6Fw1iH#;cp8oa(99zM#Pg z(6KXc4zPDx;ilfD#Y_u2M{5@{m|>S`m2i8Mjua~G-xMkl+QmoSu>ABVu)3HXbWhTt zDPnY6AISvZv_Fgm-+rPcVit!mo>!lu$hfw3sLbl)OKrP#q{N1R`y3U#ms-#=H1AdP zsu{nuvV~l^RWIY)meN&yGiQ*mL1Dd}a+?6zu}3mj>JN5Oybr^kKOk(1qv1ceZjWlY zCvRwL|Md+ug|*hmNtKI)Xy`bgwPbL3d4$;0^TrJe3wd4(K#~ee_qS(pCH>|+riKD) zVwl$Bzag#^=aJ`2h*l=c(bQphcxFkvSg{6Gp+muCq!8N@K|HlBsB6JaF`F}BNlB4} zGJVp>j9p_YCKh~=s4(mneQj%2^+G5|=i&S41u@5_{+Y9H96KNEkZ!PQO-HwKvl+J! z3NSjk`-McMiJ;|hWjy$f^bd*($A<|0>EIr|Yy2_v80|DmWHX-Evg}BCoz`dsqm($? z-4Mgb*l82Ol?~~G}DHWWoJ{a1v_nf-eORz$}IS37Oc_VoWif z=+fD53n+w}5_Ml>Bte%(x z4JV?*uE{r~)1aSOepnzx40658vh$XGG?>tMBMW37C-XqpC79pvw}uE2vHV+fh(kpEraltp7T@Q&V~4wCeF z^~wH~|Kl^BqTX2rA{K4|i!8dC+n4t$c!t?{Tv*Yc&8~w{8iAP5sa^qP6#wgKAF?mY@9nwDbtRV_1B7Wn zLOQ>bT_c?V0Olk?D>7;SJ!^NrfqhP||NYlzp=`71%IT?=h!B8P_lrlslfU4-_Cxl!#rrrQS1c-mS0&uP-qe~D zgjYua;-MUbh{u95ymnu&j2&Lvh6ZQo3cA_(dnzm3jK!oNlW!*4Z^GA7BaUDlZZr#) zO+A%xsx7*;J=V;5HM^o@>EW4vc_@wf;f+1#x_+Ud*!a&OH6k>>@s^%sE8FF`@vdaS zxq7JL4~Uvddxne*pJ8QSjz-4I9jGuJepce{Q^g3Hxh3esa3eFs#uoMvYRzMtLQc`k z&;-pu=P#PgFnpB;SwxEPVU!fbOfaquv+e{K7emM&qSlt|8ij>Ly##AL_<+?8hVR68 zWCP`4Ls2ZIO(Ru;#m-Zfd+P%-FPEU2cT29O{PK^Flv#=@_X*qq1-dNcI7W~m6Y$0} z^712&mY<(A z8IGhA!X=_t>n+S@T$4LjU{w94<8y(&;QFTZ$IJ;BG}Kz*gfwjGr=#zJNqESE zF`fe?-4twlN13RqJ;E(KSw6FnU*D@gLi3-DD z)gwj$%lZ`s=ZQY5AQ-+@MQCm0Gxwar6cai|W}wCAwK^>dm!+hnq|@@aoLV91i|}Bu z!4He+ZesUPo7}yv_LKoqK0ZF2o&Akp1~Yu~>+=KL4$w(5xY%b?x+dUoY*N6oD~bFB z8AAgAlFNXZ_NU`Xo4mG-52t3iY-pAy>GkO}uM`xff;N?+4@l`>;AZb?z+MLNuARf` zNuK2$k4A<_@&93nCDo`nF;|YYg&}HCA+0l=y362Kn|(7fGK#rqK|E4_cuf95s0nDj~l|Nc^(V5!x2TD(_)+M&0wO1UKXEoG_;MN4M1?7T>-3Q z-f5QA0O!0G1>8o^ohnYT7PZJIQ8CDXsgDLM_QyIVlvxlV%7p{OreroT3(>`lM@Q43 zA|J(qX>JZPo0yt9&R=xd69gvE-|BrF5lF>5Q0@YE2DzHUC=K?dK$Stifm47w|-w3-_+)S^HAx#eesO2ZT6(E3Y4!GGp_NxT}~Lw{L@sJB>~MO z9OOvv%EIn8_++8^x>V7*#YR^lMkRqa`D1+m|JqN%4+{;S z0%r5!48L`^dnR|^a%**l7TJRn|F8#a%iFbHzd4AhcZRksKEF`A0R1`J{7;LD!ya`b(-7AE!JlOm90P4S z8$OrO55Jzj2mU_Z(ClPOM5bDbj&)xyzO?UF;MU62?NxLB$TKs+l$`3eXY63lz&gEg zBllY4>G`HU7KIVZ%O_*+43*vn6b8KeQ%^406GMcKJrxv^&UJ|{1zc|;EIt~lMx=3D zMm^87{>ainnpPXvOw*BY{sc6X)qd0xQEm13C<3y7(#BSZqyPuFcI8;$ryUcVD*d6MN5sA_V#ZY0IpR7mAym;Ap_TV~r zb((M8&RM09MJ3{?Fh>G*O-D+Go}TG+f&fjs=v^U}o{Z=~pUoy#qv786q@R?;R9oQV zkD;Nm8gASu8|WZ$T=Y3?sLvMkQkZ~k7IpnDl&KsY1W+rB&&EQoe%Xt+cNny6_O^&l z3tASGYy+Cw(A|B0@U0+}u0lG`Ryd3zZBtO-vWIRGh8;pSzA%iPeQ?;a1$Hd~L0QXd zEbA%mQe&EbY1jMLLT@628i?e!OV4&Do;WMz6QTD>CZrJo^XOMl!cKqfa6MX-99(WT!221WgZ*iM7sdAx)PLhtiGB%$S`U>>rJ^W<2e@y+R zczz|m6^`NY?zc`8_m&v%grf}W{hO9r(Qs!`ytta2J*J9S?q4VgFQc>Vzuwh*ZR`H@ zKHQ?@N>LEyIHrFbG4Ak=AGs4G=+7^dTAIT&S6W33W3sL!>IUx~x8U|zit&UYab<5v(l+Q>!3*x?STP^ILWd!hrgLcoioi53@wl`A6rbuaNX>0rUiwz%fsFk?SzhVA;{^lAd z1X##_Y(k((>DRD3SQlALZyxdJ^6?tI#RD93@3yfem@iw>K`|5<8ooWs^z_<$3k+~p zt6-&`B_BD>!KfnVj~ARVH`Medo44;hO!12mU9nQzPCh!>3yKY6m{7wvJjOL_<>`Xu zb^|oEJ|}9GNXgVENgJE8;!5TUjetj4=1x&WBW&CEiNQ<{FF8%(P@d`u-LN;e9utw? zRm6vw46mp8w+&v-cWq6GHRU4!40?W@P4y_P+-uKdt6c$XurRVv*`UItLlAdi$o0%u znu;3$b9}k4c0HMhlEZZEhd^tMsgBR@@8`1&__il~Frj(YtX?e^8Pz@l?#E{!#5h># zoTV>w&(1m5rWizxOabr~)X;)e*p7RJ4j7#Y@k;P!CsrwpL9q9fJx(#OWW)YmCy)HyN`NwguC^D(@I^Vp=;E}`*9 zveP!7UtD)~I<5*0n3YmRofnjc zG)rwhe?MkV#xTeP4VB+`ndqV6{sVS{`*i#)(kOvabqc2&xEnN>beVuC2*ynS`mL00 zdN*M0hbFK7YT}CEO(5-2wmHWJnjxwT5Ybg1dfft(!|wCeZx%8x`0enpX-woqE{xI> zoqtk2`76bduS_d@FH*|KM3lfO>K?4~ZNkCpgjG%x5FZqZ78AKMcnyHW8!J=;JzzvO zLGjw|ErJSFo5M%voTW%-nUSVXJL`>v8E$=juDASv~e6pUU??qQY0runLi<$w31X7zDk|dHMbITV(PN`<}|lt336VKO-|n0)05&Y$w4Mi&7%t zVKDubx}Us82XT|;>AZT9BYN&CH| zQx{v)NV!xyWL1AKeC9a8S_KJIaX6F*1NxzTGO=)DKhDZD^OpfQVJAN*fIca4`}wW4 z_w#er2++Csy1IJ&@Ngx>h&q!`67wINjL?7qy&!$;V=he!@VPF2f{RyV_|5+474hNl z-HKxC{+HAQq&t>pGhflzSM99*i~x6$Ua;cM5H8@1S^=BD(wKbZPBV3plC3NHr1eSNw< z%Z^vN_%G%53X3{ULKIi$3=5-+kmEq^d56;+()dVL; z6o3;X$0u|VEO{*N5#Umr{TJBq?jDK-r(Gje^qJBbX--TlzO( zN4$ayvC|JininoW2>B8-v@mP@Qaa_u7Z-q@?v){!dzkjsDT?VY<_$| zTaVS~7rVrCv^;!Mem;RG4sQ2{_#|~k7f)`GZP_&(w#aYa++p1}MK(zP$jRT zK6+YN9*mH*i;cqB4Og84ZiEPbqvg6r2<0txwA;~BK&U9lQ@=h0rv5Xinyr$skdh0{ z0~IFm&EK0k>^uJ&rdD`(hI%r~?OUDS@^bD|Y^>kzMwZSi$HL-IFVyZ!`P{XeV?sja zcj9seWq;mHA-<%5D+3n&ukEJrd+q;n%ONP@j-G!1h_i$J zu?2iZ6Ar_0GI4uF%xr4IB864#!?Hqz)p$$tB ziHNH8VWFF-uJmn;GLlw#+|HjQeiYKj(%?S3OgQw~PGqh>-|+#5=;)Q*jQis5bEduN zwR2NHJ{m!nk_s0(yCNZnRN85o!}p`Lry-V4AR;2-;~xAAEUG;SaRUDatlq!)H?$ap znh*HOnR9^W6ZmWn_u4joE~5n#rm^?4`jFKpt_NznRI zlz^F^W_46XKJ_2_tcHh)Kuv!8ReFIU`nbWHI}Z#iNfy_DM>E}%);4$*JcscaVZO=~ z;d~h`w8F)iu4GDZcjUel=r0U&UQzW6|TlYmwX^NdS=v++6C z?**PKlfkxp0S9}4emt*zLiQmFf^8#Hl~pssJUp28+1`wgFS;E?-F{qvrk;8C`p=5H zwO=I+W0H|pPg{-Xe+PiaQ^e`^8v&vffC@xlgAnAPT$UU>h3qNL(()@^NQqQW`}Wk8 z)^(GkN6LUf3b-NGET}Wsn4h1mZY__aE%ln8uK{5a{Ol{$<4*+4I6&dj#OyECCuV|+ zbJynUhtNjhFVI)e2^v{$?GU)-RC@hZGVe_vE!;OX19gc*gg_Y<%KtvL7HD%Yv8=xo zRK*4+y-pW*rvQVpI4=CnBs6qWh@knVkO-hwre@ZIVd4|=d=Sw9U3r0fQNL88rh@Fly1i1nBbnO;V zR)smxBcM_v8hdBtG~L;+)fm(Jr?tjvm<0U3+cih`CNaNBzKx(iC__BV6~dJjudsyk z2Bu46ewOm|0;Zy1wK5nsk(Q4E{R~pCC|Z|>%eSe)$SkvCx@UU_ik}0^D0ygrmM@j$ zqigUroCj%uFK{3RQANt%rb>-jgfTluSlA9RJLEM>#Y=qxbrZ|3ddYWd+=JwTP$(Y6 zRl~?xN-!rcH?xw*?&TQQdYd)g3vP`zD7P*&v=jiP`vV|v1sRD1#8EXy=JkdK^Wth$ zc!^nNV{;76!!xx_4UKi7){Gp@UYsGU5XT_w$HQOW}u6I)Z!d99X+?aHfv@#}MX;KsenRE+reTy<9VtAyt=GkD}S8>3JgF6O+!rRufl zhlpc`L@KWH%lzA9E+9q&`>Uxh?{j2UZ<4_Ay9-;rSI!Ja@awPAf}!QWd%4uFfm##6X(w|@6J-V2$CNELIPTA6BV^;Jzg2nHOAK)zR>ok;W) zU^hL20}SWDF8W2-ZYiXd-3}nqbGGh0z@V zs@?5B^+F&xj#DEFTxqc#Hz!{>$G5S8Uktq7P{2*GT81Z})}rbwyEu|jr^@SC7tN)G z6_9_|Zr0)u7kC|o&~NRGK2xzOxdCF%Kpt>QY2fv|NMhHU)Fgn$o?3Q0+6_ay$jORt z!-J(^vh%A;mNzfHzB7dJGHnOS7E(tLxGlL&I-l8gH*0-7PWT)S0xI6Hd8G7aRQ|2> z6w&4&k3T*<%W|Fu?G{|Ic}^jE(UzUmzNMuP8lB>{c9EXxYfBQydQJUCj!!_~t_Qz^ zv)X+E(m+DSxIKg5z1-6JgPXV>o2D`ytxKTS{AH0~KQ2D;1lZd&-Q8f#wU$~O>fLyd z44{jmK_Gn5y(L#jd()bVgFh&{(>OLW&aUKcuC>!6KrnEq5zY| zhQaIT4z%!p^zI7pxAW3ApUfi}#5$6!W;$lLpap`{B^#YhFsXqbb~)m!AT=htIB~!; zl77~L#;PUYdFko2OZMCyiTqa1KD_@27)lRp@<+5ufc>j;{6{t%{~spwzp;XMPawCmi|+AxgRwh(^df~7b{~XGS})-Ft3DAr z7sViEKEtc0GeSdyen8X0-~k{QbVuPZe`c)$NO^mzm>|GEQ6P{vzsP;(0Ob>hV3iC< zSBV%-RV6tZqu}Z7?O$;r7}Ysc3Q^BMGfF2*wE6zdI`IB=r|>#wE>|ho^iBf?1+Zf? z;GgKA&&O5SRTE^vC13DthgsfF={aqjr z^D^KLK#$|}N8{`I%b)=TmVm=>;T8(wG8Vi58>nY%Wb~9?NlJk?+d%lpc{p#j0me~) zu!`#yGAB*H8*RR6is{A>lBxKVmv=4Ho1u+z^iO0!;f;x%(5p{BCyJH;0lhZ+=rf z>zytpicE$A2{?V_{@Y}luLJ^}5QayC1cPvRErICA98~m2&}umroG4}-SKJ=Ddh#2T1 zr^O%BPel#`FEdOASFa0%QWn<)v75CBCs^f?|9yvdEr%VgekY>kZyJ}GK*GSE{K9{F z2F&^7(VVrBz)f&;Fe06gK{`3pJjuSUAV*?P>^mX+7K2>>&#d?cDPc2;5x@E|4f?i~ z`p|XQvRys>H z1hm~2+T4aXqF&Xi2>|NyuE`A-p)4;&7$s*b#9#Ucpgm4A*aOV#DK8+AVjPTG%M5mv zm}A{fgpn}n%GQcm{!ZgNHfChW{}A~9opeC+-ywboIv5%m30Q-X+~wJ`;OEV#|@+w$LjhXtcJFBx?aS{);FCdf(=oWBkV9N4Va4-fW{j1skd zD~>jV!Lc%%#0vH-p%lysn_hKG12*U#L}?d^;Qwi278)P|3kZF}Pqd60cD(Hm21g&v z1g@$es5Sn~PFG{3)jkOe+Qw(%l@`$9Pc%{pH%$O)o@q&#^@KYYldxG-1({0TZJ zdR#m_PJQ@Z=eG-pjK#VaBnOpPbkYEpM$?-vbv+|&_Z75VdIm|FPOh#^Z|uLu2$(6o z?f)d{6P58XHaG@EpC#Y`Hx$U@i($UMzmp;#g5)Ua0LJ2QD@>v;y^lt<($Mf}EBBb!1#Wok~< zUSj(EaDK&kwnu|YYb~>T-ZJ=4h_42iIgkb=mvX6|r<_&kq+*e=Uw;O?DENu69n0MX zQKbcNVs3buwMOPir$81C1gh^O%tp&_JhL~DIdu++t8a6vor%6=Km5{FSiTDQnG+D^ zR)JK8cMzi6-px`z6)WmY0a;2!fFho{FbcSC0|Y1_kHrv{M$Jg!4Tt^cTYPXpwikyQ zAxo%V%%m%+BvNg()<-u?;sKS2gQ2A7@-bL>IdE}=J$`~Wu2<=Vq*)eLokYn0|MJ&B zKH%sn0asG9G+P3gau3k*N(4VD2pP|c??nY251D{?FgR8u;M^nB6tnIp>zTs}!5PjP#?{_x=b9pYwj1uXgTI}s-uaLFz=#A0IrGZ_a3l5135 z_=HF7hejgbS=rkm>ed{kYQaE}A3~KhTE~9Dynzf9=k3`H5=a_79?59Gn9g4w zm9ozg6vH;|i)nvcgi%>NHVAIHHY!y^|1cH>4pwHi2 z#H5NAIq!LLS61oMdR!Fho_rn==FF9ZjhXE=T3)j`nXR^aL5MWj4;^#?NP(D8S^ZjX zYONPn^LPbLVnX%2L)}9saL0hQvkV}SWWd2yFL@$bKFMj^Ov4{)aco2^3WU)R-+38P z1Bf%G&KEz1L+vK7Ngb7g!?x4@jyBts$9novR6HBM-Yo->r@zHydttP?qN6iBM7c$F z)^=x2X8C)!fkg?6T?=rApWS?YMyGo|m}E9q$mXJZ9qkQquhmKlqmwLA)08E%IT@2@ zTFB|{SANbxnfALCUjUZf5%j_eD)VGoS0eu@5RK5DG@o%0ZOG6ggNSa>NxDzZfEUWm z^){4^h@!cz(wSAj_AxCWN;V%)EBbEjd&mgazzWU<4}ylJ&M=>N7YsDDq|zRgu8zy# z&U{h*%kiHOcG09^00LPX#Lt03_lWr`wH-N8y8m=Lxpuu?Vms;GFm@Kc;%p>+EDO?q ztsxf#0{RvoPbUqPAaUwxNXUdNRu;GqLjVq7wY^cFa;EfYT<^>qWdOU1ExTnPMt6Z&6!nQ)MMJXqguHJ>vOVKdorP_1pmzMARh7bE4Wq@JSo-P$a;odCu?YU|q z5NwoR7rQw-YkJLW`p@+dv8lum0w8uhFxE)52NNK&*9t$L zB+(+*wpdBvP&<>Y{>y4~1rjkCB<@lW}n}|o)|>=H1`c|sZ2`CtzM~rZV3Bvc3oh)QiZwe&n$q|2|tBY=|?BSD5Tn2 zNG`{)%uvFKYHwdC_i&N)`5wL?=D?#l)p%de^N^2XtR^h7E!vD(cQGZDasO4vAtc|W zN4g{+Ynj$iVr5-BJ1w4{{oVWma=d8sE`{erJ1?;FbPZYf;X zp(khYRZ1xEh`lgD0ty4?VmjO9jJy)dF1Ct1lia;(4-1UICdpjy*x0!kq4$R>WYuH()2aI|PRo87(9aAaD zWgvSx5b_)*Cz^JhoSn5RzYO-r$USfi zBrN;}3-n3M{D&l#21|}Zz7Nq_Nld4I=^>4#d=Dkt`vY;{m)U+hpJ->U7O*f#Zc%GG zc)!y_i^I5Pi@UxcS|d$7Z^vx#_tI#!@~@mM#R%Iw2G|&Qt-}{becBXawANyOexmJJ z9tO5BCpI4+F($5?eMWxu)*mx+we!iKCsim!CJkoJHt7clGKdfn9{JPd7zILsW@2*m zbW=$5>Hmkl_YR73&AP=E2~7~190i&nNCrVba+IKwqXYp#6eWsCPE7`BGDd>rAcAB` zA_AgFP*6akphO9R1PQnXRns1s<_*uYpS{;!Yi)_E1^OW~ z+}sgycI{_%nZI&rQoVTXL<5s)qTrM=6IXW&y;*Ai5Y92U&hZVuRce{e^G9gYwVnrox=jC1|=Ex_L$P`c0ZDMQAK-08!9vF&o_ zWcn_r@E|jE;Pj03Dx^dNcvW3-vhK{PZ#)|t)DhVpZln`7jS_@NOw3c`(qRS{2Nr2} z(>wi4ezC_P0Cz^HjZs5Vr3`=}OdY6G{QfF<4C141p}xImrb)vsViu_!c6gqO3r z*5^J)*}PD6J}h8`Eh( zhoR-2OLq&l8W=2WC&+qhm6)zlfa(wk-epIro`fJRZ71be(#`vb_O0r zk-Bb?A*1I7=l2Dav*Up{vFAXXlm>=PC@^YH^cjySKynO_&%>_z^J6;h-l|<)*PBq3 z9DMZn71Bvwnp-QA&{I0kreLV@Cl??Jvd75)p= z4L7vIx7fvZ=s}c{W8{j{fA8-OWutG}ug@#*h++$sN_4>Jd@V*}m-%sV$(`vk| z=6IzyGoe11*X^AuJG{_SR!!@AH3#P(u2UOor3oFma$$$#+ta-B4?@hVt`;Aa0k~Ol z_LnG}6zP}oGMujETbpjYePfQz_Io=^f!WU|6&mRZH4N@wnxb4MgF=b=vp_dE@O;0y z_lIVqwdzs+(!RLD}2FTrl-y*{tW7;^$gU~4~j*u;?UQ47m)o2@6-A{QsLETl5< z5qpAG1#9J42A5>TsUcrM=h4_0``09yYOf!BtdJ(+II|yFF?0UK%Nff|YD7xM{U&m@ z{HmJ#zz3E(XPf13vAlX~EENvVLK}TgvIjJnXx%b<)_!!42+#zbNl`~d&*^U8qjO@i zhM#8#{pC2+cCdY%{8=ss14WGr^mI!5cqR#=pZ%{5r>nTtxl2>N&Cm&XYkBG_m2!J* zQ4aRLa+omZx#2VE(v4hQ$ugy?x0xlU9tDK%F^JVN?QB@*4qNQ&9KO`!O}ad;hBdwa zzQEJ8ywO*ZT+F_{Y%5L3!q^N12L3z0E#A0(&-!F!Afxj6mbd#)bO1#)0wl!ojB{ zlem0$V1XC!l`Sr)S>PIrEpBPvmr8JJ7V>`C1uPA72UZJcuwACXknQ0-3s58#JsTO* z*)v%xMDOqF3iWo*w8H+(b{V%SVy)w?w0$X-1G6)sg3`Z?uBb_^|I(8Y1C-!me=kJ3 ze%jL<`jhRz#5F9r=*BJQKJ5xp_6vtLt;eaA+qxH?dAR@+_fm;@6&{rT#`HMI1+pw= ze(p5s(X-i?R6G_ooV`5LZ*H+p!F-AX`%w0jHlEGBUuc}(uo245(^IYc?tpG(!Kdhk ztdI=tuKUu|r#-LcpIjuxspE^}ix2m}X32QaFylw7T`p&=Q~ zK~$KhN$zp^P5SeoIdh&K;ZXSt_;T!#0oqilazH(Na)H*2VW9NFcr7~V!eB5d=HNuI3ON-pSF+}lUZ`%L({AS8 zm>-~qenne@_tSwX0nNmmu6kAS6EnSi>l5xje_WrR(Es%9C@{e|)wdeIpS85xSQ=r5 z@T3m&4Ykm8lp4+rEV8!_30~N#!~ba&i-u1B^VcCNmBa{_2pByoz}lhYH@;>seSA4{ znHWR$RHekg-j4SQ{NCBI#xt4Vk#Krpbi|;MenwGukdta;@kPXD*SyhGhz1FtdM98x zy$#zn_q@Zs&hBolf>Pfi<$=g^erS)3g;gk}9$f|1XmYb7rE(4hFq~BD%wg~`AJpsA zu;wXM+uVm{qr)x&7&FjJ2`RTx4MkW|Sw-8ukpZvO^$UM&*sSKR~dvS zuo_Lio3@|ZH}Fjx`6$%m_q3DHV(z!>E3Y*gL}6^NyDu19&uaxt_M~u_Int=uib8EK*KNMRsC#7vhaLk=*K3( z;-Zd=nzl(q2`M;52Cj=bm}%czmlXIH&eX9yGD6k4eLf9KF-tWR(K^Bo2sX!RJgc}( zt8HC=HPz#=rh5Q;nho?a4@(n?=RgCGcUAevhYC6h2`as(WJkNd4SNOq=T~z2RhuA`3KC8CnU1G}cY}bfWHs1u7A5 zyNZT^S1oh{`jV_rPb&lFuUG4810!(pp{J%EPIl)#0isWxexq8V(&SZml1#pTQH=Ow z*soyl_M;I>6kmJ1`a02Jv)7qTxPkLnsT zf~@IfZ@|;@dBB0z-6DOkR{N^^z36I8?YH z+R!zBpOu5T_@EFO`YZW)^3nSwS#L4Vr>YijI9A``+v6#>pNg zKsZYf9G;0qxwFvVMDpuGzwI*HmleC6cNS9JctO}eN0O%;sgw+?^^i{7s=WKmTFgR$ z8j4bzMtb*P{eQZ&-MR1--CzFQ zK{e(F>qxc3ixht11ik_4&p54=(=qpIrl#J^O-J9U?WS^jWe8I9r{teve%37{CJPK1 z%n&*)&PF@-U#p=CXqzz+PowB!%JaN;kSuZZ7iI@80k(DggU9xhEUJ@&W(YCU@A8E~ zFV<|H|4RCPU<|svX=`&lrHjKhEl3l@^#oi5;)`-Ch*wMzWYk; zeY@cd2-dulFK!6(Em)T0QauQXZ?Q^OWXL$zuP%8~pXx8;e;$!I-uGt)?mwJh3Aw&=rY0D%aS3s)ycGq{}qoyK1Qgf7U zZ1-F?!EjxEyEr>gEK`4TqiytAfV@Ff4uBg4rWjknsU?4PZOW7mc5Gz2sADfX?XpV9 z-542ALEOv&?HjOq?ojapQeAjiFyC zTVj4&t~Zv3Q=*@I8S>bT&9X#MDk%mK85Ql~txED8q`y9#lKwP0GmSs(-d(Qpa5Vji zd>voJUT6NKf%Uy9neTJ&4gi+%aQyK2Q5V3(y5PHHQa7@eaY+wD$`U%>idC zS6vaQ4FdEmS0h8daV?ZjSp~c%-Y?AnUVGw$X`Z**%Iy2XV)rA*AJ<$e=N)7MRye@J zD&-E{%~#!y2OOL;xCpi;6Z7x#=?Tdga@sMe9QtXsPZN&lQeJv#@d07@H@l^s^uALtCz(33=~Pi@esM}ewM+;&jTP{I2&ccKH<;Le=5dSWa?L&X$dJc; zia3Z7SO_yr+NYw|;|3O;%L*+6<#jt4eL?SB&_)>Kdy7dvRDy3sJ})w!SotH z8+hHt`A2@fD?atPV^g&KC^1PIaM4Om5FRyqT!fBE1Lp_=5R#CV?E5>}uIM)J8ssM{ z6^dOp9G7(Ngh}FF1wCr%y_{H!=7)!5sxPa5Pkn|*c4ca7P2=9TEUIf zzFVATYxxc{ZMkV}jtOE>)CwK#anh?(busP3|{UI$G;wd6G?M0(z?md_*)HERE(htRn36MmpA%9eM562m4F&lZ}BbRwP@Z6 zt}vWtQhTvPqp8sAmu%(xxT;#ysLqU+&t3xbo6JobhQ568(qp&!SO29(4#4EK?0Zsf zWY7hO>4S7O3f9Se|7+@1PHCIuRktt(^nCY1Dv~TcjHwC&)M`KNNbAkfk}l) z2b`qITYX?og|wAS%B+l(aB3U0Hjhk%SJl$b&!mKssZbpZaomJLor_YLtG7M}X}O2h;1q6@HOt1(%M?8@{2p`g?zg8kxUy4l8WF{2MQX+ znKji}--$6FC6Omzx-E-H=YWL$%PWsF+(;>rt7me7nK6n=;miy5fYoC? z8QRfs@D#Fx%CL@)9}AD*mBLbd|B62~qo{4eOXl%TzJpB*J4P&;QunwHU;ZZzwcBfy zLr=$*vswe3bMUmy;a^Tt9rw8{oblrG+=0d9q}%*62t0?Mxyi}fw)utjWVAlIF?6yk z#O@OV1?_vwkCFETqkPx4+|F`8zA;y=L=Z|L5alEgsut-d{J5A^E;`J0vWflX;s;*q z=TJ<3FS|z)SZF$jy~{(qE)Ql=Gpqty$rOC;+D*G34E^Yi zQ@P&!6~O#NYIE0mEQJKLuEk^uD3b|g&e+~w?6KG3hm&#Dg_2{O8K2zjwdO9lip6QS zX8R|ni5D4hjZJOulB%BEKN0B}aVr$((fTrmz9{Hxg@Tuta3Piw&PaE1F}LhA^GC0_ zuar9{gc?YH!1*`a=Z};Tk>*Lz)}S&GR>p^7VPb+%116l;YKRAO%>w$U zK`x?pV1F7b#a873wKx-Cqj|cjXU}BdFFE^chV%Woq)?xh;juFDCiJl61m9lTFm1} z>GzWwT)0v7g}D(8c5OQXPiQ{MfHY|ByjZD9SM;pkM9X2r#};buPGbBi)383JtKt%P z7vH4!b=haFo8dnK_V~qFUBa~YUGdvMANze~Y}>h;Ay!0XR%%9cjQdG#owy^n1n1s9 z$S@Qjr&O#w?sklpKeg=io4^D4Y!c&#oE?H~+jCWBv{V!-Qx_f2P2J`L-?%QhlvFM^ zEh=swn^YqK;kIMxa9o|ED&quCNZjg<&i;3%CVFkgC>tt<&&wM+TOBb!wW3s!#^yK= z)Gn&FPV%>06?(}brTC`F4Fe~xP&*whw*^sjaYC7HJm$D1(Dt)xQLLtGJX>Xs)e z>g!n{;a6r@uBbE)H}$id)9%uSwh3M7N4J%;Gp;4!vn2egKFUWdQGFwNa&S(iG+>Wv zhWJL$vzSuxqwAIgH7&kIR`3@Bfy&zD*-rxPWw17?5HoA?tE*t&)}vZLyLE6&_U50x zV@rqHKV!e$Xh#k9qmgz!LDhXxh|t;L5x>aw-1HdW-&7)`Emeke@T}21ZtIC*u7x}- z{E{SIrC@6Wes?lKVXZdWEgu}GvWnst$)H^v?Dn}KS#e=*ZrhR!IFM&Ub0hG9nJ6~( zeD7oD%*`Wg3aTyjo^`sVJE9g%$(YjUZJigQk6)fwIA!=*0462L$C z7>Za&*{UBvw{oUE`ULyoNlr;(e?LF4Hnatvd_+2|WM#@<(G-OdTf89onL|zMyA;ij zcFZ92l#iB$+)Ou5s=v$9C_(j^UwSxT__T1jqhLkci&a$2Uvg@D|7?XrSlhqM;aj` zalrL!w74Ub*whb;CL#Z~6R;2t8evJ!pClsS<1qd~K>M{Ku1EY&_upcIXYMki_n&A( zD*xb-0zZq29r0Sj1rY#6#`I8(88Bir5W@_F{6_U8`jDla+5dS2s((DOVuXT5!bS?W zD0Kps()rV?EY+W>GtpC99xxOXk_=X$xGDEoJmG;QZ1c+#L{Pc)?fr^;`1=pUutNU+ z5D7dVyI+PCGFQ$2LQ*uwy9APdcua^N1nEUleuNF?2Z}{tSz_bKkiVtF?`q`<`9*U> zcG4B^?f~w;QaAs@cZh&YheI_IrK8icESB~ERzeW|{~DFQrgnW(*cRvRUcOW{Tox#*Zy0&+%LnU|AsDC`45%C5419L zHzELeWG^^)jb8WTvFDmA9qDz@U1FX;#ZF!luh8|qvHsj^^9was*h1%%Qe}fhiLdVF z*?qV6x@h+_$NybjmFG|6{Y$3z!GH?-aIEHI&niUbhpS?SeswKRDb}wQN_+dJFE7nJSV&H1P`q=G3)R zBPerlis{EiZ2ZI@%k4c-b6uMA3b$SGueRxaTXi`-!2Zei&avOO6X+HB9z}3_CDVygKezRf*Ze9 zHsoL5SiW=c1EytXK6HC`uA(%Ms3abL9lh1}Gs&sP#jvWWIn>8w-ant9!#Bf8N3NMJ z6%*`&AF%#ydidRPNbbW8fe#PAG>dEh5DMN?ulq55kH3<#cWwXuyUVad3d+fdKL#nf zl3-5FvKIe3RFDAn@BTy}=>{e*Arut@M+5eynO(-OvjGWZ|MgER6GT^=>+K({BaonJ zlb3kp7k~QSel->9CGwS1uWUZB+=I~B@zDDA?SJ#7zkdz;4Kzv*lVe;uzXB4f?t3+@ zK#K>}@TfwL2-y@lBh1IwUE5zKl}+sqeL-WZd0l& z>x<#wUoh1YtP)Aha|+td!C*Tv0VE>CA&UbnP7^pgz^IAnzATli6l@q4XDw;=e}7}h zgACTyU{}TBfKB&E@6pa(H*nuV0Rf=z{i!l*h9|hrrlYk}1(dee zW-y1Qds1^igA9K6_;hJcbB=!T{0mk5-@hdIZ8)BbUKpr*&VoQ&8qvtO9b8@(%FI4L zaw^Cgh2psQEczoa@2QVV!w;Oiys~V)J`8_+;8gl}baXT<`{NEHgU0rQpQ{ht2z43o z87F++4t;NZqY&5=I8N}mF2i@NQiK3>qOt39?`)ojyo9d4L;48Oc+1ELGZ>My!-xPV zw@7Eh8EE+o6`{T|%q+k1?l}0sGb7Gn(70E=4<0bG>EIl6955Ov_AtOx1IF^*=O11i zhu4w}-G`0lb#!xUV{p^989boK?x3b)Vd&>15_xTwI>U0t6uFwzvW zYbUF`IzS?Gb1^7Cp9lJZaf#g8Vkl8Zx>`3!7$>r2@yFyk%H?xhe{unS4QBe|KmX%n zq)+fT6Si|nN?JM_h;CBg1#0}*A_iD=6JWJ->-(3|AI`bf8qfsoYw*4RimXFGfPD#y zHt6LnxyFF6G9AGCADo*ny`loo4;pC67``?ywQ4jMj7tIT$s6dqg4KRdX(>Wz3&W^{ zV^PY$8jgYzTogsTN#^?E2@I;la<^^%=K+|z^(`(G%F~H3L^Q-8|E=xPFw9WD2I}fB zpC_Jk4nI3#R3vr!hGUFFxA`Nb&}QAtAW~pf+V_qE4S@+baWCN&h`5Z&3Ru*Fp*HKv z#>PwVSgYztoGE3My%hvr(wVwh${#@nUhp+xrV3ikg+~z@^{d=+vb&VdEEYjZs0`x& zJRDO`c;!MU4rJ9?2KhHT6JH(%tpcSzv=_L&h*tdfT5j-P@6jD@lxUiJUaSJ*5BSBN%SE7DFQ@4PD!^IR!XAV$I? zFM>p(Ri-_bF%a)gOyPeZctpRPRX&{yv5^ zq9D1gI!Z*q!3X2AvfV$$|L2{!MG)YNP878xtbO+E%Dr4+QxRq;wki4qM%&w&AYcxj z(q@x%=J+a==&=H={M(xuzn8e)9C2x2kXE>5=urY_Xcp` zrK6*RhG@hO*?uS0)K}&}9cNVtDDn57e_y!r{X6OAhN}t*J`AvJz5E{sC;pBSTn2fu z401jUXV0E}0!HP)fn|bLsuD=fR6fmt*M#fpl)d=%VKlH4T0fLoPkxhUu(BHe>AM@7 z54^P}m6hNpMs0-V2tETk;{gzz3C+tf>K~im-;7T5%FKY9lT2Rk3gNCJTDk;%yoNo1EI|u>?w0?;kx3%$^*Dv zW4FLuxS(TD+#;=sy&{(sdYJ>LS3sO|h;a(aAR|H<*7WVrjnBVPTJ2ozF zvOL(NV)V%JJ?Qfmp1eb3HxjIatt^~ zIB?!yY{B>d?2%@hI9C-6gaMV$K^SH7TjrApR);N3& zVuv5gpPTjfyPhT8)k05w8N7G@&%wQIy1_u5LC5R7mL6nu74g3elZKgeJ^u&|iY4UAQqO53Uqb|$T3h@+ z_UBXhbrr$A_@$@Bld{r})6)6Rf}s7Q13=FMT!jkUE_MBxi(gKoYH zu!k^Ku*u{gF*-^?K_OsX$!qaWhz?`|5nyyaEDwUF`JbS8%7POWGA(YP$L_qd12GYU zH4IK^Z1k^v`zr45o8mLfZnpLFXyr$yE5g~fi;S^vR)K0qQ5?l!hp3oXuQOn{z&N)R z)Gr9lUpFlA__?A@A5dDs*dk?9Q_~4J>u>#7yfgL_3>jPC_;7z-LrIm};N z1|c5Jb3UCBob%7HKem$Cn+zCm+OpXc^4QwBi>;5V+AyKvXAOEF^X90$*QI6HCKYPl zvVR0A#WyhQm+It~C=<=Yp#-rKD9tpX2Su~EL&}n<1g&`R$1M7ptc3$sj1o1L3ygn3 z>b7ljF*}CShzHId6^Jn9kVDW~Xhg0)iUx)R-y|GO8uw0>k|#5ZJkDUzRTLwK;=XIUfXMHeZaB}J5g15{?WZ{J3;E@a@* zQRNVm#2X-Pa2hPp+k`1W0dF42&?D;R7&<}277k9}0q{9`uTBv_OLXfUJ3JTJqN~uM ziRip@k|$?P)vMF(VCyr*k~U(k1d_X|amYWI!C@4piT?GteMIRvHbtr)yG!$BgN*T7 zSgy8!ZdSHJg*~b6NR870!REL27ft@-{8Cm8kr77xg6p?G6s*$69@`{7aJ~(#u02s( zRlLUQB=qDhcRl4XkF(BfVrLa5PWo(Awssk@v5&h9gOwEV_bJXNheL6wOgH-9Z(RU+ z>!imMY7`;r!dvfKpTju~mZB2afx{`5E@Bf>{{ZBewjg_(cOsSmzeDhD`dWOIvTz57 z+2l3iL`o)*Fb=qz)vH)7yvvXG(d1Kx12+;Vp}Q-=Ft$Mk1cQ$^1AuS}p2X^497*T z8%G#b-aO(*NeAqz)(XFS4b;;O}g+R0A*kIcWX|B6`470OsN7JB}}m z)UbUU{jR#d``FcMnf}|tbolR+DV+A~j02OVM59Z45f zU=BQLe9@DkVE6q?_x#V`BSb|aT)=$Oz_BY8kd#?Bhv(<#7tNc3(+v!@C@Ru=^X82! zxa!biK|^!aC_Vln@gW2$Uf84bM>Ak zHTA@J;O9qIjTF{B4q{K0mX=?fB&xz1>!N#0e_m#hzeetHQt>wPLrid2k%T*}b->}_w~$~UB;`1$!k)Oz8%@N1%HwbK-vP4&$3-czM}z>xKa{aUxP-+WNb z4)-;@$!A;=`x*$Bwl7IcD;=}O>Ws^*LV^1*+vwozoX0nJx%=+kyM=zaK&*3+3=FbF zZk|IAMtyVHuIbW<96xMFrcBNvm8ncnxQ|;PEjIIkir|UMxIBoIkLBg%NqK!^fhs8X zZq{q3fPjFa=r$-Ckh1AOqX5nqVhtPzC)lDCxvSKi(EymFO4~^@H)Tyhb zi=Fo*g>w!+Yp@0j@v5kG=o+d%^-DFp14E;2AggQ_-)o@z=cWjj64EV9WZT&|h`scx zX=+f=U?5G!>tjA~E4>2s`hs29$;(Y)jNNzrpLh0UJh9jIs}fKB2BIK&=T9PG;__vd zBNS>e+njERWg3nfw9*~~MOx^>)80SZ@z2Dk+l^Fj6?sJ(V)5DfU2;s_0`E&X14;i# z-;<`eo77k4n{LfTu)Vcndd8k}jvhNuEHE)9>~}c&+w*LG)E!yx=rOrQ5KV!8O25qW z+b81G)YRe4?(S}wjcBsbXqblxJzT82mxc95!T9)5z=!hka;USVH>yXTOtLL)zRT~? zrNxLrkjRt2n2J&H`8YHLM+^vyS!3t}pB?XJPzb<-dV|$ge)=vWwr%wwX>!Q`3EwtI z`2L7pbw7c&LS9*pmP*lSaTQ4v%#o?SYHU^GY*~e$a*bzzxq>W3AV2p4F^7^W&{Dy| z)YvF1OD2aybw2?UHw1+hMWJ%vQRN$HZy;>|ldRcHIqdzfyW8J!*7DPh(jH$X05zER zgTuWYtW+>@qadp-!$WVsRB2@(R+ai|x^VFtUIj~vr}9Kg@Nx>XgwyuqywS-UbIH2C zb!yT07dM7o_GBHpE3p~ln=*4luQwz7WyQ~Z;hCgAM(2-+tSai&w(GDp>w6QL@&+Uzn?EeEn>x~bDZ z@-4i*_b3{@J=!($Q}ndgw-E5{fr`zF*Sr!1H(eHj0}rt*#o%4YPa@~XP2oYUFMNIc z_C+)fbxy|XS8f!{O9jV?+7np;g6s<|452s>&~d`oz*X-t~t50MzIThH}G8OqhAXbt8doo?9RxQ*G=6Ss`x%35}Vv zwlaO8a>5w9zC8qB%8mNu#2P@ufXc7?+6kGx`;S1@!a&eUjeTGGHR6?leCxY+GVEEj)Q6Q^*>6y#MDeuK&6rn^H3fXg1%T+&C z4xN$EvaS|bY+}!dgDU~%+U-JMwN|vaMU#AL2Jo60#KlALTK5EL&91wKY3ZLjmUSxglsq z#M~$OM3TxilN=3EM8FJURHHp+D6(HfqeC6A%*dM0RJ=hdf{OaaII(>Kt%e7AaA$r> zPB~x(-6{aGytdbLO_1?&=FT>w({4)r0D-W;Ds85SYZ^E6IA>Vp~Oh4mH?z_J@FWL;eXi#0p}D$&_ogJa2NiC{8w%UOh?vLb8yA^qp!YS0p|gS zEw31z+7PsK_&T0u21a*-K0%(%ilRv{-gCRZ2B!?WzxZmgl@p{y!!?@&gcy3^^uqSV z?tA8+r(Z48@+;JTCSwe_c#jvACLGqd6n~r2Qb_}bsTmHmgH8EBF8D9u!OGLK1|KiN z?_Y&=tzT0$Kn81^6_&@LN{yA0xopxr{=W2r&UG1Zdj8bl4HG~Gut`ar5Szo`D+*bo z>Xjl58%#>gEtj4;kD3D(#%^$&Xk!lyvnD4+b(pb^kFSF*?Yuv+BjDG?CY1VBSB*BF zr5j>3tksEKa@N#ZSz%Zb*ovzyy^h9N|MW`p{o@5rlfVnK&|!_kQ|G*RO-Tb%Dnp1r zB_JL)El`SF{{s8#iMg{DpL#=Mv*acs%o}De&6HmGS z9fsx1Tj;N1GAa@E834hA+mn_)w>yOy!4eq6`HfF5y{fTRg?VLRfC&@?u=gwaLn>FK zuXeW@{D>`<_`45tHzefE>05Gqc%$^!l+=#0Gm#sBr}+|>jZo5&Cc&oadJdCshpz4Q ze9hOk)9gve>u%cI)3~$7&m;-p-B0i$jQSz4FlaWh!B1^O>An$tLf%hYsmM?%(uJIi zX}y$VdbyBzW%Uv9%_Z@J>(`oPPzl^Oge~h~%JZQ{D`p_ww}Q9bfIkJBJPnMR9%Ncy zS_p4xVc(F6Sl{o($54sAFFT+Qs1_dtRM%)G+!@U=z~xTHD0A~#Xjz#sgr`SM!16@{ z0$fCo?OcGQT|J8GW%mrBHq}yEYK*Vq8i1Zi0G}Zh%&xp&XT0MWk*H>SO zURts!WLRBUfnl`8M(G^1ymv9Qd=)oR4O2V4XRJat`9xdKgrxlil|x71Uy?3<*r*WU z-C@AP(`#%u-E}Oo^q}$J3fea9ILhFF{F%a7?{nkYK!t}S$~ooRQ29Wjfu`!66^f(% zLZH4z2oJr_V*lr+yyMW?$g;2)M2O7PoJ43u*n>~1<;4D$C&`J^Gdm0VA?0=MNNg(fC~SWscH4799(?!CLGVRqaJY#QeM%@VcwG^gpw{#JwP zw$u^?c%j$$yZdu}R8rrahF*xQoLtotczNJm|FG!5w%AyZQh03BzDJz-RYWI1;Ss>m zeFSD-MIn|uw?7UKPsJxh@g&5fKq}QW--LsjJZ9l$xHX!*k`E(%{{V@83g{Qx?bz@n3;GA&F{8bN;gj z;!{z<@2P3~zds}-6j*f-D#Ta}DZb z^s?LL7hojFECCY$IO;I&=)La=gS<8vN;dG}c_^;eS>J$?!-(tV%|c3m)vy29=S&9t zs30Jg<#(=O2Q{=U+0z!MOhp=ab5VGPtcm~3Ly%0h53i3BtFhseDsY>y?n;sL4X^p$ ztfZnQ9LAs2X`m>0`jtV!mpOfJj;t4xFNgw7!!RRrgkoSCq6lj~fC;NXdjK{_;4Ncp zWM9UJSXZZTia|374+czZc!I>CYDD8@R0&DT?+7kJ2nbcX2x$W$iE8knP#mw~4Cb$J zkzz1|AbM1(6PhtkZZ4T?#K%EWP?U$r$aOJDefVC?w4Yk^7J3b^DG-E{z<}`%@bXZ$ zWV-e72?homh+ZO|oUq$jXN33Q7y04oB{yAd8lH1^$;ip?fP3N5rBhK&=_$VC9LEaH4~O4b)JDv_ft!YTtE9+)>xs z^5DKS>s7N7IACu1`ht}vPc7uV6hFC6L(>y3)A3zi6^R8L`lZ!r7;U##`qu7euT1Yh z#b^g>>T+sF4Lr7F22&2B6yCK)i$GJAop=o+6wx2o)WU#2brx;w;6S*Y^CFbUYHN4< zsp!Y=OuYYG#s9#&k%eNL8B|V)xqaKr#>FKbVkZ9pV0i@rY+_Ih<%W=sV&s%T3Txc` zHg`Niz@Q5H3{_`S41bM+)~{Oyzo|->@x%RWs(eyVDv`iM2G0VpNSw$B9Q+L0_pGY&^G8QQ^z&qm>JbHe{ z&meWwHvab?e@WrsV<v#sxrYJc84o zZ#oH)x;%$g-3m~=*;vP)KR*nb&*!j~Rgx>Z@=rsu8V=29$9r*VJ7H@Gy{hHWG@NNCkw$9GCUnO-+PO4lv^`nMF^-|-Tu|}v|zfY!V z!1~5NbLPyF=(TIt0B?qdrmIrvcp;;emDOy2Zp@FL;Lm6aJug>x_g2V1*B^t%6zTJ3 zT-Cif-I-urFaM3D2I*!jZGRp8l@0!5g&41FdL)$nZBXvf9oyOY@X~vZ7cnyyWW;AW&#p3|LyW16Id?-Az2@b;>YEDKg9U z(?cW@Jsutx`Y&97$R*^x|0Mgq|NFgXk+#jVw~fgkg;f*-YT>EbzGWpCbE~FTWJtWe zB%^B-t$L%A=^+@#5PNKb-BaTii89&+}qC?K^oy^>;8#2{feWR!q6hp!T;kWu{)E%wWu;h!ZsIse<#IZ z?%TJ}$(aoi<&zo0*7C{xn~mOgSf($_DMR}jAMqgrTQzWbQQF_#7IhjFK*$^p4h|Z3 z#B%FzLupg!a0O-{wS&lp{imBQI|tl3M|Qja-xhrEM*+wMPxS~b$Ndq#SL}jKk6}Bn zLr0z%TD};3z~@4tj8NMeLa|~Un?Tb|0sMLjZEbDXbXR?YG%^wF&9ey^^LFuh2*5=5 z=Lxdscwinkd8e4U7qVXSBhAMHS*AxxHNhcRtWJidNI$x(oNfWEeEBlOIH z?QS%{qd!*;O~`>w6+R_^fU@O&z>Z?$)}^hcjl%3ZzN#Fl6{Z> z0|SM@kfTo2>l??Cbhe>#l?h(L{C`y=p7OWIXXtO@s2isrY zUnR#U2beAp7f8WVK8sJql+w1>(Dkz}vi8gWrx#z(o&(bkU2nb(hfsUva;05IwMJ4q zIhg%+z+mc$-F$$WzHsB0{a}869RC?DE7sw9# zF`n>mK~oV~B7}b~k;pJ@yD(ZTa4Rr+92a1ckZN~xU7hrow-17$G60RcAfX&JRrZaA z!E;7_XVXOvLqcOXz^=bUDQqRHp(}RnqX}{DRCfXGc!d1uLFltx?p=kLdf3AhLF7_Ns3yZaIQzm{JWqu<{*l{bTgm$`{^ewPg{^`s}sH)lmO=HNVfX zY+;o5O3TT!`xATTBxL-+*K9*R@$t7f-x6UGu64c8V{b>$j&g$z()@oMb;x7N)?hyk5D`%a zZ#^?>>hJ7-6Cb>I2%yF9GUnYuvz3KB)IW3VKezij5;voMZ=benupUKy_it^`MBadp z3Fo_x{JHV}xFAs=aA}ZbC|doINVa*yPr2<=vL7P&+ZX+b8~*(aazLQAq3<2V`HDHn z#~b>rxWDK1lgJ6Wg}=7;E4gNWi~Q90_(O+z|Mo?{Ly_WPe`Nb`{N6sI7vR#~JXElf zBjB)vk2y(eL4R98ieyL@{DKFU_D6c`W`!g&;8pxDU-akyX%>Dv__qFm;Y1E0m-b_K zb^Zi;4ms@}BopQQ?G@OHmw!ZvR++(@hQN!L$mX) z*fAmRaitF%26|l>VT*UtSs(Hk)Ah>INDR)))rM0V4RhpMI&hu8cecrZ<)6w zQ2DGD9rebXdi|I(r4KLJ+t`Ji&;Id3MNY%wQJ3UyU?)*63lb&7K;x-1`2_WF_4OEt zZ&|LBt)>;1qO8#Ug$6vaOcGj+gD{Bk+HfT?jKX~ZbS1-=D1PPa&AO?fzgB{1z2KgG zYuCxX>yD|-d{TviMKc1?O0?#)$JQ2K+~Y&tF)O1telU7$>W1g{WMf(mSV^a;|D@cZ zJ&gmqn}6@J67~nfG|Ti z!*ym=&LY5_g7o~gm+PnHD(OG^Y?$5JqaPpdhqfRERu{A@ErgmFk*NHF5>-!m0|d0!Ip?k$MfB$7GGmtYGFXTEs*mK>VA*gJO^5u`u0FY(LVU59xK zCjLCx;D+;mz2aF?EFBhGEuOmQIkj)Zve8gNzQWx?agw~$WHLg9#COB|hascx%eu(t zZDh(#39pvzp#Ae<=hgBR#0CghD&^OP#Nnu)3|<8V}b zhJJ;@gGY!~mKG02W*#!5%mfMnj-vO~*3ac;QQJ0Dl`=9Kc4B5GER2*sT zEi(Ej0Fa`7!b3m=2}((!6}$Ylq^69e=ly#cFoErF`Ua%CCjr7Kxrx-=S~($iU`SyR zYr>jR9Rk5OB~o<>ST!(!SrM!_Fx3pMs;c@SOO?33G(rzEvycu3N28dkd@Ov|>CUI` zz{nwHF|9LYI$oG{5eY2O6ZS z$H_%M3f5b#Z{Dzwliwz^zHGRhNvxccsND2pbyd%ZcFlJP>dT4w`DmbV6^~DHFkxkV z)-}TD;cx(MMDUm(@L5Sglb|QZ$qik0ST&EpleoaTSsq$(jC`R2j|7bP>g^T=OL6{y zx6xi5=q$Sx9VftSoYPrD!ZqhT1ZK5}!w&(w*jST)F$)|(ERzPJs3?(RUT|zqrNf?- zIyKo~NrL4@nl|8{^4{HK?MRM8C&l}<&?3L0JQz-> z|HbR+CgW9?EXj&`@eC+Rp6`(ED0Q}~?@cz@lUoJcipfeb&oWpYwqX_CB7_YXrP0a$ z`mE+bJ6J>)E6l!D(C@<-F{~Y~h9(CFNJsN8u2wwQ-KTqqNjVMxw?S|hbWRZ0ZllR8 znE)EBW3{yZ&W&1r{-}82K$H2kNko#58;UkE5b(L&7 zK5IprF_1>zM-x;SJ{4%@etipA4<_Lg#`w;1}+-Lo3Y=VEK zq{Oje-cgjEDgDGff!nOK!NdZ`rx{D9?UB6z@k^ux?YVBHu$*Mep>hL#r; z1XqXn&GwxD%(=BTjU2Y67bHDP^=I@OeLQKiHF~-BbS`a|K>c6~Km_u$^Ti0=Ln^@u z2p&F{#ZXbR%>&5s8R>+iTf?#615TjBk!Qp4XBziA7e(?@zfIiCeT0a@x0y>3ZYbHw z_AS?3dH;#9wz;NJq=SY_2ZYnszCKD2=63+PVpxU6M*)4P18UTb)V_X0?Cv7tdx1Dl z=u}osCxfB7kE3#2?~fGjREeVl63ef}x+?Bh7`lRA2s4ZvF*v}U0y<<-D^cMJu*D+B zj#FVfak_ah7;oOGkgg9O(|=&MGV#3VaugCpp=Be`&!*Oz0e~z9u$N&E4PhG5q%Z3g zs+H?%P*g;d_(A|WC`HmUtPUivTlZ^e38rF^y>xP*LGN#Cfc-w6CJBb*ypUp+YuQ{OdzWmxuGj9NM;;@n9Q$S1}|C;Euw8WBQFa zDK}eHaTM|OD+1@#BC(WIO`SOVWC#m|$|Nux*GSIh^5y*0#YFDBX&?JLk3qKdNHG|1 z_+CQvQM0RcfPe#8D8YPC>qoQeBp!^eW@}aFvnFSiEl7Qq*sJX27=j)h&p&z;;j`OU zN7D;GZY3YZXvb@Ybpz^VC=o4C&aEBwYRw#Sq;&=l9ui%4a-{kRO?rC)NYO~$zx_L# z{%5^`rVhbHd{&mt1( z@|dw++wO{0Qf?uiQBh7)N*B3VxYpg0*K20b;8E+W(Cybz@p03z^E5lJo%*l>kOVo& z9Cy{7Fi3ddlt2aUZKPdYkq5AWR^2h&qIr@3^{rAE$!$~vf72;7uX>+o5}hwr~mzV5kg=kB=|fXOOvBGPXJn*8|#@0*C4T68}~#vClH_hy+NXpC?0m zwzKX9Gq#h;vj3As2svZ_x+NaVsKcDQ6>%8XVghC|Twnye^msR##kgSP#T9CxljK1! z*+1v5YWO-`!UyR;O0^th(a`=kt4A8O>OULyI^{J+t$Vmr>Z(O;-dhhvg@*lQ>*?Vp zdb%G!CWp^>ah`(#r&#gTR7_lh{hbq|?5aid)BhiB-yKhNAO0PYW5ltt$FW!0q|9SS zA!H=GkjzxJaEy$sLS%0$qlmKi%19z)N0H1z)^nZG{kyw=&+GZ`xnI5Rm)qTOoX_|B z{d}(LeZ2?S-V`02ng%{9)@lPTM!^~ZQGF^%TJ70pe(ZNCy9 z6FaHknV@zxsGy+Wdcdg~8S8sRKwZse<#++T05SE7+V|kNINkhFwZL11#@Ra68H#*5 z*4sss8ctuB=dniX!7CKww!x1FnwymvJU=@M$|zc^9UyIc}_mJ4Hibv~x-*!y!R2LJ0u<7)?CE zK-^X~XrT?S1q}T~*dT%)9VSe^y=gNSt71bFSaF_|U6l-794IeBtgS1;I2L(8*2K?1 za)$0tpR4p7_F~)ABdMvltz7Ukhp%CV4$0AnJC)K)?9Y_7xiF&=-df`77Eti?se-2e z^2E%*)8zJw8{#)iOpcEv{3a*CkShs(V4{RowJDKODSXLuY;Nk`RW=MBxy_#_wC@aX zyF9=+1{blpq)16$2$MVi&^jPp_XQlq z9P$H@{2@;&M~fIlBGV$=uU)I0|8^{dcfjJh1Cu9mt?qj44HC3(8+-H3UmrnU3}Be; z2E5rf$ppX+Q_fVX7=FEZ1PfV%j>@VfY!GId#$s9wT3Y`3lz@(BLqf35omJ_weq!&v zPByu$G!9c%K-%h?oAK5TaCG+SblkQNiCWDr{=kQ&tL6yCaQ~{n7agL<0|`>E6OsZb zC$zb~ZHA1!^~0Ovlo4O|(k)5%o8~Ov+`ioX%0JY2MoTvE$EQDPn?$&8X1PV*ms(vO z9?fg0_iqxq*6l3qIU5O0oa#@;Jb<17es6_nu4h-me+uQo0lf0# z$E6{#9g^DG+M0yF661Ryo$f-YbPGwLzxahNfvq_B-%SWr=y}{$V{(d>eWp_n`Gef! z3C$n%mx%ZJ$jFe-&WA|vkTyZRFf4YZ`VYq)f%5}F$c_Lrw_gA{n3zp2?VUas5RHxG zgj`=9vs&q%RGFQDHn*nW!J!|LZ?i9?7~}K5vVu(w3~jyg z+ctr?1I2S6C>nsp0nzaNmA4j8?C%q&vAF?7uCRclJNV*Az|Z;STFPL)S&w%TYBnZVmz(f0Z8iv}=V7X7+3P`C$E z|0nwwVnC{iY^(C!;{)+YhR+_T5!D#Jg4WF^;NioEx08XKGIg-OEM9i)_-nQ~Rhs?% zy|cq3Tq2)qWxZD#!fAS^V1xj4K6c;Tt&u(8VBH<7T8EB&_q@_RDLea?PrH4_$6^JV zmdM}8=37u?txmarKbvt69qd=dJ54mSLQ-Rmo{gmKvi2|%{ zswd@ahN&GEA%h@i2+Sdwo13!*`T#KAGST6nBq{JIy!12-W>y*SA0sTMAY3#6Ta`hW z(7{f_Cqm%hz{tkrmcv>t7b3?}eK8nH|su@BF2?NOZeq$m#3~5E$V?&l7|te=J|QvTn8c?TT8GSOJm6 z*RO9On~LW1NIA{hoShR3Pt59002;V$m*~2`YXlPr3R&4@tjveHuf3FGxIK=WC6+@y z03PG%56|<4>-^Hr#Mjiw1O(Jt4n131I0M|L02sCx^vwd!0m5tq+;zprb!L5``O4Wh z&7w<9y#s5i0CEGpUIkO|b7vH}>bmt&w{snPScFpIULZ^-IQ zUOM=1sU`NE(+}BQ00O9x>+IJ4y-*gWWzZK#e=4xXDyo25q5^p}li*MQk-L*H#$E7Z z0im?q!7e3M%=wUw;zhe*`E_bgc;v^I$99r#=K+bMaE$}Vb_;UdSmVM`38+CG7>tn@ z!AL$EM7%KbZvZKCf2E66P5qpKOl6%5ArPFNZBGvVy;=9e2zYjs5l978^F1lbfN{l0 zJwNL0EjtNr(S>Op{ztt)i_TyE5sqCwXPu|U$3a*%J=w4~vKLOpn_K(Lur$_Fs}=0` zVX+y;>Xr*=Flv2lpr(ej+Uf6$2@qw?c&%p_SO^>SkdE6|>th4giWHxS3b2@g-1DvQ zu%e|;19wstctDWbD+ReT8auUkC}92b_4SiTX+b_&iRbroRQ#710gNyhJz_RkbM}ny z&Pu^I05(M+*5=7mPYWc)-odvuB5bu|i&rW3o-7rgn_E>433V08V_BbL_(YYN2E~q0 zaIpa#xu&5N4b7E(7B)}j^u<9eU66`TPfg#oo%W1de&JUjKdvR|*+M9|$;qr+fHzb7 z0`~xCEyRA?6BG}$Xmx;@-jnNq$%aq3T$imb=F zkX`2=E&#bSBF5?j|0T_(myrC>E@HAq7!HmD2eCNd?hof_>siU)vf6*@dsa{AW4v!B zaOPoE>B*ACMPvqWFVN=Zu9`cgU8v#Krv)F5#tRe{dvM^|5QATC6o(kMp;7y*xhU zY(G^{!s)AJ7VH8tg=2jHca$Zd%@A-E!>9gGJ_~SPJ|sBJ`n^yHWxTu}2>ZFKJ`S6I zq~inyq5do-1w8x_pOXjPkVlVrp_&hb z?5#|d$dTa+hb}UHbhcpI?l!ZtO9#ue^DAmD=rny4I94Ef2zPdwyG$wJ$t$40g+d9vbf1}cfOw;52>{Q4;N!_yZc_fuuJU#YOD-<1 z_Jm&i?X{}HB>M*pyAUy0ugNupZjNAK6Q#O zose=p4N1Sg;Kp(0LfXEE?s}SxbOVfu-#Op9rGHTR@YB;-N@@TjHnwcxEYA;l0NQ{S z6gBFi1)D;oK$Je>VaD9G~ggWbo{vO}x ze?Vm@f!bfsY6F7Z(qZo8sbSS|QSX(#erM43&Q6iJ;&ZWhZ^1S=TVMYez%R>w2)vU* zjuRT? z0rDiEWwoUmy%z5ocQspcTNMcQcvK?UejyvX(I|boxAi7TF9}#E`YJ0cAzu3`NFknk z?o79=SKeOYd-Bv_BLHMl9S?X#fgcb7XIlXunO3h?;RvTbAE6*~1_aF)<`F^%wLKKW zjir$xQ8*GBWUZGn&{9~}29Q4Oq4Kk=PJpU~YG;!r0{IF<4S9BYa#U{L#*FmcHkK#M zp5B8E^TKzQ_h69S(9)vJa2i;tsy@YMXUX1^l9B?IctB8z56g4PmKelCl|6U}wl8C2 zV|oC3a#Pp>uLAWh3<;g^AkW2Hu^>_X&f*S?o}@L}a!MHRieITV{OeAhlmw3IcJLEI zG~CQj0*DDotntXu6kM86_@)-IDg}f=a(O-xsh#(!$Tv{tu>VRnlJUA-P^a5odbNP> zOB-qiL|do*_Mb=~K2+U23!(G{Pfy1 zz%=~wUdUJ<3d~OQIpxIEj;;Zv*Om;M(+RY?izi+5U%A=Uu+BonBiL`e7^wgZ^D{)u zNdQUGIelThALapr&_z9TI{r}{FtS_i(K%(HcbQc73|E^#N=51AwC71$x4$(^Vzq`1 zKi}v%7c9fm`zKN1dXL+;r7ZJcn;w=buIB%1@mZ(gM&4M;zHZs`*yuVPH$jL1UwZU7 zU77U5YjCivjXb+Ds*{1fTEnW8R2ao5MFIq>K!M2bS*)|rw!rU@-PQ^=wECjA>0VKz zBqS*?_1{{ZO_pSM>o`BU}wpH!qB>sYa?aKCoR1r`T~(i7JE>DQ zPV$d?pH9EEVt-NhLCqXBO2FcGAfxURDz19pWW<;GZHWne#=#A>xF1i9mlr{Xtdz)O9^fS02WL#UV^Ugy`pr==JKaN5i>*6sJL zA%4|U>D={UNT&O|x~;(SP6nd1OJbCI-i4)TUR{;Ss_$esM<*G;7$C+u1IS2)uM}lb z+Rw&bvd}iZ7K@)Vxp;{$Lmlh_Tz0oy^sll_fwaZyT^=`x7w-bq(Q>%N;`95b43Osl zo&Jy&_#!|)E8g4O^WiYjNr$m(;nW9E59tTq+Qx)bRaF)0#nf`H9X+D@6)WL;Jr{T9 zKV9l8xS`D?XUTsdcz1F8dFxq~RO__TQn9v2q=6d;ZY$W70IL zJ;x`%zdg@V?gVb-+7&m9AWuwxxjK?w{9=vzEZO8*xfs`Ijn~Ts_DniB1aveUJ7TXu zJys5L-kwRXdD2$sB$~J8Lk<2?*%R$zi`u%OWShg9N~z-RFC!L6r@I_gxN;vja7{3T z{PykJ)W??eC9H=-g(N6>I}8GU>;^^VZ0oPjollqA?j_}Xhy=}{e_h>y;NzIr7wfm6 zET)}!yb^Y}JiiNO|Ib+6OU=IY+26P8qyawk(M17cX}G1Da3>2SYmbxueJ5irQG6do z6a2y7ijE}bsPfBcUBH>447FDPi-$W{8T|u6GiIPCiq{fOQ6B%u{-))z2mPUv1S~ z__muW*H+_Ov+?a6TRD?RWJF9EtG}EbJUirrK&22fmRzl+et;93@ z=xj|R!))~|(;c^+6Vw^rl5xT z0?gZA3suhpeObJY=25`_+*Yg*uN~|vK*DK=_21___W%p-OioQ@9GB%_eDb)g!DZj_d(uHsLZx;YVmj~Kab13X?;q89rU{#r)V#R? zaSZ1f8bz#BmR!UX+6VA&uG%heP*-?+Oh?|8bTa?+83Xto$}|A)STAcJGOz=PeZSWt zAA9!>x~*99Aop-vPhg+|A%I<@4bV7!PBmy5Tp+m%D{=y(%n_m(Ae*Mc&<%Dae3dBX zNPxQ7;VHyMmx5PbjycUqV+@Q?%-f?;*Z^!IZES!uA6l#p#Q9P{w+(>~AQMXsS>|;2 z*!u|sgM9xC#gN9%P9m5b2RyzBL4y#tLHYRc<9EV;iK~&i_#6mz(<}**NV?*HK>Op1 zY%6ghF!sEJ^FVBut zTxP&n)5|b-*uQ`On(I6=*1pZuFjpgLzQ~~79{LbQsnm?X|6$wX^O&2!Qyc+~w$o(m{=E znD-T;m3w%*>~a7W05t|*w%t(AidlZ9PooFUKm9zwMO$fiK}jj-(YwWHW5eL3lhvLjh7$78gn2i-bAgBpB%M zTzWzqf9guu$;%a!D^pE;h?wHbFKlt{+_{6L(g5>J6*PBxpF%dUlWUlW8$4tZ5rcLm z_Oh!qp)r;zKW;xkJ}rgvKtazcl*g+66x0CtMRnczOHIz2OAJ-A=@Zj?1X;nT zoz8M0jU-Kc)T<_o1(LyD#l}#B)je%44Q`G*T=Qo^%t39V}fbwgXzOOef-=TM7} zw-=n}!)cV(3dN(JO&l>P+~TIVb|<*$!Iz98yb_|D>|b-m9B*21G$aO-Fkuu^}!cXvL;m#WHf@T%Na9;#i}c^ENRPQtp;*( zvofCa{<6#ZQOGxM4PC$P1dgq`4jij|TpOzO(h2zYC)M3X(g9OD{mK^7k^qAg5+sk2=HH|0D1v~aD?2=J9 zs1KY{4BsLLQS!p+Mw5@xNuei+FB<$cdfQ${z+N6Z6#W znT&4Q^2>bn+6cZfO6s|}@44ewIh4o!^^9A9&rs?@&DkqwSnKCy%1&O%i64r+TH(5U z<}PcU;Pveys#}ZfGb=jor+rCMPhO4?OL6Ue^HVF{ zDp6)yGm4Y)ULWj#&9(H_aCEa?#OIhatrV5u`zPq!Qp%WW&+~!v_Z31Ltj7-BOwIMM z$gu+GG3buJI&}wq2{9VpPB|b%t(=poFpf@XY3jKNj3Ce?aYv5@Hf^rZ0 z8~#7~E7*zN_XvE9P8p&kHUlCWko@Ka!U)RVo&GxP#Z_mwr!1f zL)cOJVEmhs&UmKW_wTpdpl4s74V>9+K_%r(u$)1{#-n&Y^0G^wJpr|z?S0CvRrVh0 z_N;*8gjcfJID8A=m6T{?6{-q|T4S=>a1FuUBr{3rnPQRsQ205I3zP$woPPA(ek^@av-mamN6ux%r9sQWy8p@)}!YmcB#RnCwfw&dH${Br-baLZ_&}^d8_xF}U96r+ z%Ire8Z<6*J2n?RhzE|+55Vt?#V6^<`vR0a2gJP^JwRwuDWVdei-QEwP?tTk+z)r!@ zWw^Xm=Y5Z6@lug?(`JLuaOGTxLxYpta#!>?hgl$QO!cKJ(wD2{Ypz{SMo4(9p#zS; z(H>-H7+Sm%$7t5Wp3$*G**54EPbb>YT7G0YPt%U)-tC3a`HC59$EUtdH zDPyWq=E%A%QX~s2;&ygFRNXpQVR|&GJXh-LMmAOTW90^Wo8*rrw_;)G-iwB#w)brW zMuVv9m9`Gtu*DwoDlQOWJ8eD8iJAqlcQPBBJ0R$TJTl-}O#VTpS(c*Ox;v2^h!dd? zW>#7MeKe-acav|W08L+K>fxcVz_RX0LTlLG&F-@iS-7Q~h@@xy6GraYAP zROxHchfM%27GOK3Ng)^mJU8eE{>Opru*h$|2D^Ew$88U%!&k6i8TbPV{)SkK)A$Pl zPT_w|xI#;y7efrVsQ$jD@)0NCr)K3v(P;S}zVaWq_*Z==Z-!+d9Fh;@$FPQlw;KNz675ZMuhzV58 zuw|kiT&@1!mJ1o7uq^}=otPr00&NQcDSn2B(OMLY$v&|yz z15L;Zn&qfUTXf}=nop_QJlnHbv$-R!CbFS;QwwSIF;^wi0+eZ@_t zAuGg$pS92BRwRuT;m5j@K5Z?A0*j2kwK{uDG($n<@ZYc71NeJ?{X2L@`kv~ z%=kCZY^Y`~6Fvdldm(o*(ap~<(H9^>@BZtL{4O%o3Dw-+&+E&TYpNuVhyBA!M-Eax z3?|z8#bi<;LOT2|26=^rjTQH=Kgf7_Gy;{Hs{DfFpYQkHs|U3zD{5A%K~Iz|nhK$Y zP$w$Nm-_2H?;@nNa4FEK+~>deBveCq+!q>rfZ)K#bvcQ(U_8xh{+7{FjrCcaM60u5Ar~$ik__+0d270X zwN)YNTc}^}7EMCv?j_g&4b_b#sTRuMNpQW1I{!UUAVmsIMc>7B1D5Oed-QU!qLgSt zVKr&7Z!r9GCH)gQ4mtAQfBZ3AtWnVzQ=IRDhp#GSXGL+`-qpK|7t4r4h*Eb{7f1c) zHHH+?ND7U+g{KZ*La2%TY=U=R(cpN&tDDC!Db4)njnr&m$tXPW-_;#nz2^IHMTb{) z9mv&OVaYhEI0rF@*YEdLl*hAFhaW3|Q~2ueB^*NV`w&(iSun->8VT3b>oR$y>aQ#Q z=M_q<8L+DApQ*JE4wo5u7T(vgO{-j)wSznw89N)VM@jMT1;8N>g_a&wikH7|_>$;) z_^C*7A+S^)vL{a{URAYO z$$3HrfDa-T=bJYbfzOT|^aHQ={XzvmY5q{1Kl0?%h{r@lk-^NJGS?Rv*Un2L;zgr+ z3a~1f_Wvy4pD!BW5@+(G4_B8$5|-%TsTufBYQJ(*-}oY0UmXh^W-6XZf2+0&lO1vG z<%y+Mbo_s1w;KIqz zXYlNXZ%?vh?+YEU1;LVMo$B@^u+)$B@x|gg=8Qe4A47PIL6xomwij89N0pPYpc|N( zT#C}gr=)>j?Vf#&nDKvZ(n{|1K0}vYLz%;eSKfpoG!hvrlEZ$C#J!L~+9~`_6p{!9 zb0YbP+M_z@^1jgM9A(55FWbRnn23y7I#j3)5-TkZ_Kzltcilz-A5c8^&Cl*&i3b5y1hJeN1?`M)nLp z;G8htv~Q+1dJFprhuyQHYk#)A!`~En6Z?$yTo70OefQKe!@jxgM2#j!rJN~Og^?kQ z;7^D-jv;k(cS8a0NRGvt$%Cv(vr@nl!+3f!@R8{J@Qe6^-F32nz(9Cl%yfGP1}Fi@ z6}4=GgaO5o%z6BE-@Ywoe6XR767q2w_&F0L*QzDAXFXB{{9^b=JLepA_11KFu0Yo= zFjHdg3yYw#ffNGz;jkCpJ3p4rC!P$RiT!`AGm2yG>y4-C@4JgSSH@lJ09kE@;#)KO zxXPFskCpq)IWGsm^T5#FZa@fNSzJFqKUj4p!2Bu-o)Lvxp7jd&!v%mtO5PRr!r3}% zvXdC#5|FZZ_f5$OTZ{T`UnPYHBk(@cgkliUCubPrYo`399QS#N@9j-G%7M{-4AL{g zh4)X1iP*&K(N;#%Xm>^PQsD5?VOvj_?bi=QG0TLp_-^6AKtsesf#;I;=?tNZu+YFh zeTQi$a&W3%JqDBl8)@`sx<+l|-ZCJJ&4BM5msUG>nm2_?rMe?aYW5j3*xzqm6&L)^ zqmxLXeLp&H7ON5jZip2yH@O7Az*BjQIq@qfu)$*E9(1xvZ+3xzVGhGbi5cIG&*X{8 z4-OK0#ZE<{>gZN2VGs-98QLbdzjs$mcrKpAYUhAFOas&vn}O5q+W?APi{e7HFeV>+ z`fEFkh7F$`!%@J9au}_(cf}aS=7g9=!J`J?djprryUhtLWw@Qd{|@Wth6JpqTpeNY z8sIcVa1w~DM2B~>V*?*l+|rNJiQqxQ-U#T}d6_V7fW(9{37o`y5BBcFd8ZJ*^T=$l zgs~ic3n>wb#HM8zH5F<0lL`&wult+hUcI~Es#?ekz96zMZ2NHs^0e9j59qen{C1(2 za*k_?=9z#U_9%JrUibNbPPtGS91o22)vXp9GYI+r!Xeb3ve&ZzFt-ryg(m%B=%|Rs)6)=)&@VY}E|*7~o?Q?(E{y z3YcNETwO>9lcl|_0L3Vfn91c+uWB0i2Kld&316{ zdI2@q1@s)Jjl9K^O0eg;fZz8&Wy0ZhVeSXtm%c*D+&GJKE}S4#S##1$`oVS^i(jI) zE|n&jB}RZ>8Ero%m0iqJ4))@)UFeC|qVrDxS4`ZL3#4?awy0U~)tS@gA$#Dv`SH#* z!N92oNK?4+LsfQnMG=hS-%4aT3(+6za`wYmMz#Q&VgEz?GyxIm*G1sHq0sG+jhS^L z*$Y*-yGDGPIgE@%v1N}kOrFH9WsiwSo{?Xf70iIJT1J>l$iq_h7n{kwgK;8Bkj`1z zgWa3mX>t?Y{uxier@*AoSri zq@)jbVcGuFp|l8xji@9E2$U4TMxg;|1QB^xHOVMELk$Japg77jVwq7?a&WR+Uf^8m zN^Py%=Il1%^u7`=1~pNs!xfJJTR6dhGMKUL-Tt3aE0x8cz(1;Feyr4%LEzDA*0*Q# z8X~>+w_M-caC^3T;^OIS`Q;W$;VL_p>|3`RR`f;tW?FKcd%vD=@R}m#P(g8QeY=A9 zSQ>|bTQ$3L>R6tD5e@-C@;vh^+@K@Oo!*}w8g!c~fJ!0)*%0Xw#wwkzeu+vJjI8Za z<9LjP%J4S%c_O?w`c+j(EKcdBK6?Y&3Xhbfwdh=BgvDu>;9|>tl8Rm-?TCnmXrtxo z_4^;P%wG`2^Bb0=7pS@f^b63Q3=BETd%-Y*Kwaw8x15y{{u%?@_&!5M!SxrxPCM|V za1rpQZMr7{W?Tw3+9$`o*Uz(K03ybNQMTCn%rcd^d*TBx(Xr=Kk9mB*j@OKKKU@}- zBiwW!CT$@}YGI!(A2JmUv7s9bTm~#*B$Q2>%APO_JNOogNc?oS5EE^9<$WQP-qD)B zPQQ%N7k2l6ei`JB5!!uMViAZnRU%=Kpa2C~xU+h*n33mJ@DpJ;@mM5BLb4kR2&|0g8wO4 z-F@*cOjKg{tj?;8PKKSb(mw4a`&G^l_hWTiR=kaoUi*B|xCWQS#es1Z%@0#|5KR-G zLu0Fm*mM`%`YY&O$!C(I!L>P3E`F-B___0)gIb}RZk>R*onaD%sQ`OA^kVNYta&a`Aq38fA+4G6iq}YV_Lp8xOvCXNOxBVGCrk2n>>iXR z=NY*j)l`0U55pj_Q+gz#6@+wrK}si5?_CAC%S3q>wpBdUwGkic z4vv6AilIn@>kJChh-W3c)|FxwE1gpUy5RMzns>CuHSyp98hR9(Vpco$Ajb%8w918% zq)YSpVT6%pbHo2+hd*wzax52|;d4(pnF^@)e{of}z%&xqIe*D$WJHx^tJa$a0pnl( zW>Xi#JKz@iJ9bIqG!IA<+I7EHl$-~pGYmnF5lT^gKY7^3{obF|@5@^=qpqBYP#N6i z?9ZLHN3G|mwwLnTthPR=&7am`I$W%GW;?0hwU`4_nz{6xAi^{h zuZYkF<>o1GAD{E>1z>NMSaLzyRtmzh2WTYMs(R&d0z!m&G1}IXz9B6F0l}Y~G$>rU zo*Qf_h+fBUYxIko zOblVE@^b_#BQ3{c_yK_~FUEN}xVM-}pU1OrJ&0MrT8;Q5Ci?iJAjuyZF+myq9zyXq z?4Y=HBP@!A<5z?eThRqMz01b|##Sgepk=ts9OKbzr(B=+ni&QZ*p!}%L-uFBXmt&Z z)t9$&PinnCt4M)`Qc>3?P)-n{yBFsZI6rFJf%Xy`G9wJ>V{z?|C67fsQlI&P)Ai-Z z_+9P3^ieyg!ZS(i^uY%8W>#Y|nWNYNupGgd`%!`7OKlC@Q{$(FgpGmk(S81?mQ;J} z2^=qK^wy)9HzJWk+)j8NRvo*(j9fo^oU4B~QUr5`mhSeP#F zs%J#i4frIb(N%OLn|odocH}Si3-jyFQ_T9@Fd*gTmy2xDJqJC>`+51l>YNf5TpWM0 z(#|Wz=j>>Xp;) zOJyFHT@Bo+Z)n$hekt3c#BslzK2L5BkCc~5$F0Y^a`xs+kBsdn%}oho(evxosq1yEs?r0PylW5Z@F)BWl@kJjRv6F5?^r)Y>*^*JRDWrcO$l-G1 zLk&df_L@FEInv)60FZXS@ZQHPz}uI$X`Md7KX}eIBAy2~VyD z0*Zu-pyFjdF99@{pglb~O3vttiPDx3mhAuIiX; zjF;Y9XzKeq@>pxzT6lzoQc-%uhDuh(_p&7Ub135Mn~^3kGC}t=Q&SK^J71M-$>UO$mD-f(fFJylAyu2Yd4-e93M`c6I{gNW!gixdgtaIa;BrWcu(T~Zmbc~LJBn4&HR9%)QCZk1i^!E&j_BIBzxX$25 zGVGZ7?UhS8xAqyn0B&rO^`scoZyL%vf%kg0zbf}0QzqhRYP}O8VZ8ZirS&0qMBVA!w-1P`0!fmL&>iAbU-9~K_u;IVyzEd0JB_ck7&r$!zQLj1KFyh zo!?h36(k7<-000HO(dJ`ZlB^QwJjjLM?G??cdKLf%lmJ8Ld6p!FL2skyD9DW%Bohp zoG86_Pnp)7GmOxCu1s1t$TzZX-6O2mG#)b<5!;}c>Mh|FkF;DJtGu(Ey4p9t&YE^~ zyZW5}-CiFt*|^mbV>c*wG>R!aV}(qpYt)8MEgHw>+>DqS%~d5Jdm*B8Hi$8NwJPQ+ zFCZ-R;TbOkLM}oJaO)+>wM|wq={5gwNnk zWX&HZt92a*u|*8ha)9~?wh!%iD1QTkVt)I+hj*sI-gLW5fA5f_N zFryV5V)ZR~rB%sIGq;C9H#jP*jo{##QR*z=@~7sXRHE)|A@F9DT;(%w95c{C5l7H2 zL6Tkj`}-)11rCg%i%tPxvj2M8ngism(Q4KirQO#y?O1NC%9wk4(QY8(Bs>{bn?x@& zuNw;oG;e4bzcaLJKrs-tzw2SffJ_6LZq5YI z+k2ObyyR*UcwhAqrL%}qOiSij`YZ7sqMeiyz*-T@C=_Vbm5OIL7knqnypd8~+NW8_ zm}F1I;Co-kdpe@$A`|e#HfKgt`RF@$-8BLUmdF5yNblh|7{>l#-|9 ztE7Y2-7$={-p6i*J!rf0=I4ksB^-SLF!lb%_i0Mj8R1$z$_49E;rBCXXyXz2`0LZ( zy&k8XCZ)d3Bp>#!z~Ne~2_FfwH0^}XW{StRSl>W~p zizl7x6hBuuGuGbdYneUzrOaHca8B@^1hpBlLd;oRiAtN>M}wl3cO=a1mw8wGoXLVh z>H>`%WLt_%ANEdxj;!_=ka^!6yx=u+eFp=g8*ng_E> z)z9ASx30ptIEK?gvmgATC5$_-t24Litti&R9&0p9_(lJzu{apJ(|}bbRqBoFhC#m( zzUg1j$S1_Tp`430Y)Cmgia=!JRzB59_Ze)>7RQgty9uS-@odoVBn}r)p`p>WoNj;1 zV3u7_8F9P#vV4U~Oc>tx=2JpB^jY43`kw1)tt#N>0gJmz}uDiT$e`|^ciBnVLwX2%MD&xN8x37b_AI`^ZMs#oY= zA(WKsnS+q_2H~bmI5;?5F)1yHCQ;06nvYGvH2`s5xSP7>9Ie^li7Xm5bMy2L7PkL% zRV8w`-Q;U;RGh^4u27~LrOhGk6?`Z{Qp_$#$qe$bIu+Npf0#O&oZF_fV591HZ_RI6 zrE`PsMq$w}EDV07=wXC5f{>In;I)@;a3>ejvtRq|rWaJ=FS_2J8t z2|-lH=@I21C^xE z6wXuc$`Q@fu#31TUt0Rl+i+aM{qdE2jpH<}Y~&*Q=ehG#$@9nOO{&uFNqw%6BNg`{ z<~6yP)ts5rYNhmhKVrpIPr+{9L9OQ&3_|7-1hBK9_^xN_;eUSs>A<@(#3;reds(W^ z8+#JYYF`jh7VMKh-*(0H%>9@cbuRQXlP2`WWY+D5y$d3;|9FUNvl<}*7qu?!PRd3Y z!o)B1`xCv3|Jtbl{buz*mfS&MidLY6dAu;hut~~%S7U#-{e3>~+jzdKCqD1TG;MA6 z>Q}yYA~PA2(YAfNsdlALv^D(B`dl60KoMa3gyP{rFf;dar+#0RLP)-R3?QBgJU~qw zHGT)C4^CZ?Xmkg$P$UW6ep-8qAMik^@rf`@JHS7JInc0#plS78Xsg+^QmZe$8JL)W zNKTU`AZ)!EgSwc;K}J>%99X3~T>)d02jDNuK^6b^4m6#5R~kK?TIz}PS5k%@Fw^+d z@k{^tc+805oA;xI2K?6WhE90ROz87F+ic3Y@2*(1kJ?p-HGcnO5cAweHQV|2eQI!X zkZZ|HBW(kW3_QAyzAOBG@x{_fLm@H%)m!BbzCsqM)XG9pal^#H3D^lRz<+@(Mm7+1 zl0gD!fZTzJKLFkt<_%~FHa>n4O!Y?~NP`okPfX9AJxkwm`T0T+9P}fAex0EjC5y6} zOsw5?kT9#}KXaktIR+&lupn6?m>AN9cAgqWLa>*K_N@08}cjga6X1|II_ zll7?nGM(R#k$)w(d{lILdOa(5{ z(VlNuDgu7{cGYKW;RX_KYm;9EKCMAl=I!4*O(qIPm#>uIv1~-b7?$|Y81|n^0Xv;tdjKXy zG~nosL-{`J$CExoDm1gKw{5dO# z2tjTu={K+*FqLEK@kt;1p`lq8XKQ+nK?dh%wolF9p@-*60k(?s&!XU~q%js4wTPlg z)FI#APyFW){eFDd2jSP8onr+z-;2CN5+;aW9hAkG|2oi+Sat$x_JAMk7(KLk z(u2q%A4)oz#+e0zgk=bZ^Qq>DLTdi_sqaG*^+1G+6w)rZNIip(#+d>}s|6;STZ=jQ zF(Vl>-Xu&5@(q^HQ(A^kLwx9b>9NnwxHn$9K==Y!6c7tZ)sLjT;q zhhKmUln@nTM&3&l`Mrgv39Ckj=sl_n7w+eqRME>Gc+D|YNSz{C!I3!zt>3$e1n06MD&Mn~y*v^zhoJWW5 z8(@o2e+(y*VLhsQmC~=MA9vh?XW?!EhXqg)XLb#67$A z8&E|K;Z@fpn1l2|;@0}71T(f@2*{7pg4DD$K}M#O$uCCDxt^;!TXS#xB5i_O*fipM zFmqJYKo$;^oVXDR6HZorJ{w?$w$09(J4T+lWeN%z@Z@%-FDB%oocG$_f2p}u1XdeK z&blz`_}V*7zBXe2z22n{OrrxKUGUKaU2A77iO;9bPh7>JEdzneq5?GQK|-4$EwrwS zAOcspJ0)`tZjY%?`tKs{SsuTc+5LE1^wsSp$5v1m;KvDKOa;fs*ZXt2R8=MsPN3D* zE!)_tWE~8_Hn79;tP;$qUDJy1vh^bw9qi4<7XilTeI#!}jB@#&)_SuweeQo)3O6uEvX%#DOqnoje(otV8yrPg>E*#97al>j4bJcx53QO8_} z`Lta366IvNiR_<+xGBTub6Qk9+I{Yh+8gPs9_}C-hLG{5WG4xsX(GyMkm(!)Wx9lw zH7P}ybR>OEBv@*xc3Spl;@iH>egx2vo?+dU*PG|P4tqrm3ds-Kl4w)@cWBW*bX_~BL@G}vm87)Fu^wJ@YbqGgc-3d%!oPI;I$O}mUj97dF?7% zKBIIR|M{82zRLeJJN)X=4|tgzNMr1llpg$Z{opgqljyzqHy{XCo5K@#4WYiFkx%%0 z1LL@fD||lwR8+LJ@09IN^#piT_UnqIM}I*Zq4M2uwBMtX9Q;GN@1(jM(Nk9 z3Sy+>|BD#*XM3!H8M~zuf9C3+Z#dvt($m}jo_*(7f2o+dWJg}&hdns_BE``EIXJdB z(-~2#U6*SPKSljI7c?C}6jYZ&r)~!1%ltpzST(ffX=p3^;asGG9Ed|~QOHrlq8A7b zBJBFMh}cE{cYW2M)RS(!WN`R>BmoS9nsM~Rd2#POg2idB_N0IPpT#kOQW&3qYtwc#BiJMaH~hV$~w zsMYf(d;hE&r#zM<_3hiX!}Ub8T5Jb-EUzwC1j_+6&kgX*AcqvBMkp;5_hkU?hGNk9 z_JymyuPQ4OCq9n3vH}?Xd9a9Och6}QPdiAt-V{5C9(vec`JY{ZA`DB#jITO#`ZQ3< zxGi!Wm+arKggtrMzQCy@^+TOl?qFr+d5(#jL+KJG$=*Ba%?Zc%&jQPDx{og@4>_51 z4<<<&uG8t!!Ct+^j?|5c=3vJ>;MFBMdTc{HpcUGUr(9{Gc?_*l2lT+0X@IPUC(md` z13q;nsKRU?OO`82;xj7a0AZgCxH}egUMBj~lfocavq1quzF@Ezka_-%Sg`so$fvR1 z@2F2a^bmJM2}UlW(18$v)AnO&kGHwP`OdqPY9#1(;5K1<1W5HzA_OBF`}W6DqL4yO zckvojS&#%r3=(+S1M|VG|J)FnoXE6{QYgBZ{3#_pPBK*W25d=p8A-%vElNJ?bV==9 zIJ?ohxh}9NJsNhM^nJv~B#|2v<6S|_S>i(%-L48OebA`-jF+gila_ky6s~%rd>LsZ z%&2)u7>8+OynG|TAtVuLl;$lWHu5P~6>#t@hZATX0li{Vne08R3vmdGuROdx`$k9P zSl=apy`X8r11?V!jhQ$3oJ<(l+I|h~YX#hW1za~QflJok!|Ub`#Vq!qQ*nb*q(^iS z)`T85W{C`J+}JTmNlB=>=}Lo_7^U5jAO~u~^7_8eI?yK)wek8lvtsm~7?N+y8b7~w zK3ibRC?B+vUkU3eyS6Dg?tT=;sS@9}9>wuc)BGFw@HZrPM{Xg!dYHm1 zB4ewF*=lY=Uo;;jIMl5&Pw#{ZQY6|il_rl%{-I!OLqlRFepJTv2GM4>V}Aurtvt|{ zCCDyN|8qx9N65!^l3T7KGLRHhRG3WVni7xo_qJ0lDhMgB9|F}iHKn%wY=)RaeW5-vG&vI7BPUhJF;@ZeLw*{@`gQ};4O6XUF%8M5r+t2kfs-n&olF>Y-TqJh z&r%e|CcKcQ4qa^!0KfzDEkVQWtkFS3iu zRD=8O>GjJ&|9sBhUreXfe_=?_zIvn0_N;0DRT3hb-jKa{t@Pwg_jhXo;Y8cjeO?_L zn%750LwRA|Yq7<^q$6UWV0|v-)QE&4O=xeQp~P{`Ls$@gVZ{}FbSQ=5eT`% zbR`rUAMbf{^6Z&go-9ove*R4%Psz?IM@56s%p4T5m2O`yh9Vp5s$wp~^FbNA5J)C_ zq)E3QOQ7BFhdSR@!U=-s0-%6TK0ne}^!$gCV zL)>Brs+0$U`<7mUOCv846_0sRhK(AqT_}#d@caX^{n@SK|2tj({)OE2Yo}4QaE@T1 zJC$nKfvX1Q8|zkw)z_7?5%^20lghni{Xdvq0pU5>xd3KJzfKAcI$ZU02$x?}ucpaw zsl9XfYwKqN6#|1F4a~ha*2^dbh!<#(t-6?P@k)=gz_=K=0ry6C@Dg-4nvWpXDM?!o zpx7qJpAiC6Z~?;-)<)#3JIjt}{UEYZ>W_wB^c%;BsKj}qF%n@+(nh_5pP|fsl=4Z!1{+q{Wu0mTmUw`u(1+D&7@B2zAN2N+%a9{ z2HG2Tp^F>l+pw4Pd&kqB3Lg?FRt&aH7PpUItuNHh2-rInd^lV94a&m5@7}Es8TR^i zT41V}FX~I^PtGDM=a087K7T#q^$vl8hq|@+^(9r-&D)PgS`|6C(4lCnFX%&H)PGi; zRXwP6nnI-G;#6a*+M~Dq3)II+NgH8s-^7tymMbFZ$W9!23m!coX3DY36j7V|cCjdL zB5FQs3;~g3w{T2b4p#M$NIA->zinKAgM+bwCS#HG^Ycq-}=TfjP}INN)uV+ zk*3K1!`@qlRke0ephqM&A#9{WU;`rEE#*cDNehq?q$H)Ka|22XC@G;RDj^`!0@5i6 zN;gPI3lf5I=RWFpJm)*#jbHb9?sNa}aE^<;*Lv3*bB;O2m{z5Si#ZuePumOi;yE;g zS0rrvJ0nx;31t-*?oF?)-9&qAB*;lTa zU;*hsDV3BOYRsN0ZZABYhC%MXo)i5MaQ@o(JgEOyh4wjBTRPdX^Zj)X!gie2>$w=q zJ&5{B1sWtYyh26yzmHy6xcG>id-e;uC~eQ4H0mVk?7-7slS&ImRTD8KJlDMz{r*{> z-s^`@-oOeV^Z9Rbfu#%4!Iyz+bB$gLJ$jHBi%lO2GN(AaP6u_^yi{K`9)({Rpf|VJ{ zioh#vjbysKwLCd*0h1X-)A<{{`k*HypXRJPXGs)gF){E^32LkVnNKV@?8s!~XHH2Z z)8R?k7si>CU;FnCH<$h|N}kVq^n~i4od5Ybu?vLk&wBI>qkv=kQ2!OTxByA~bN~Gn zGVu=vRqpv4C51^RuS#pZ*nt=Uf((f%A5IoM%~}!O`zK!5rWMP4bTg;(`m<`EZ6vgQe8W$MEwKMfW=+8`=Ips<)tRy8Yy36_HF zEZ(}D7$6Jr|H|Cr;vH~VJxdfF#Jc&RMh@yMI&4on`8Jj7B@@t_(l&3~3j0AmkQ zh&zt1t`0XDEIgAWX3MXc5iLXKVh#?lcga9DSKpJx=D0jam`iitG&iX(EoT%I?35f= zrDZvjyn~IIC^;d{Fdw#fdn|&c`K9#Ng-0>!#W&I;nFUQ063!+2q+D-@AVPuWnwMAM z&Cp41;OuO0nzm-i`F=e+)C^lkAb9DqR3p(b$T`}n!NqU&qo*kSIR8*Z6nzy|EewJZ zI(AnFCvy_QeajBa9V$`S#)-C`#t%T}c^A+nv zLLJ&Agx!wL8^S+3=C zYBot1W4d7e`*MQcw^b*XyLfg-){vEaj9~%{u0neXmAtf3&-c_GYBUX?@gMx(u3AQX zt-E%aa=MSLvAbJhxXC2FuXPzx0uBOh|K`W%`u!JbX1qwI_G9vHcF;~==grzLd%RXo z8)5OFpKD5la3Qc&MmD=+3*Mw!k>E=LEQr)Fo5(2bee z(l$2C9wG^omh%qy{tjr9jS^{!2$5Q4kiBE=6R<*07%d4Y1zO{9Pbv73&WKgQ;2d?i z)1w&NY{&}1z9Kkarb~SX|Jq47JtR<}oB@SKbKem5P(YaDZ-el3F~jWPDYY9w%iB`O zYHdI&VS_rzRA$MqfB9f8kGFCap$hHrpu}L0F2B0`;MVvXP~m78r@%gulE}w|n(*0- z+Djc+^7>W{@jE7FaiQ}|z_Il3=F63i-`CXa=PX?2VTkHMR zo-QK((P7PoES)dv1dY_uC&saoih2(rA+bHZxGWK$0qCR@b)(^!l~>aG+y%=C+~o_1 zqMO(0AN(WNym$(kA%&);k6=Op+Vu6xU?-c)T<4R@U|^Dr z;+G!WOn|afeTTq+5^xXmnRaGc-srvd;&Wg1N$FGMz)=mvqCkb=DZzjLuDVTjKv3sZ zFWLVbU4;V*z*MGZ6WV7_MH5HdLb3C6WQm_+5rV=-9IdW~8TGF~=SPA2wi=)=({TmE zKmR`z+W;O}rOPECSGf#e2K)1my(T})w7=lC>A(QAS<_MTpSnpF948ePalkN$qNu_m zKLC=$jYV=kI9nI~V>{bWz@i|3Ts8WMJSi|@m17SwqmO$$93B$G`oixY-?w}Z zt4TDXi&Fb})aC&cfihnnbjnn|4SJTAtB=pJyaU81?#BJ|NB@}@oWggGJ z$+2&N>yq?!$g7=%g-@!*P~_QM#(ju=a4#k9A7aq|gf+3%*bocq!~WSYa6@JK zfT;T8E=v7eRE1bA?6QlIC^xx^Aa+1WUQ7SuX17FhJ0aHeKkKnxiM_(6R~2QkRv zj&ZqwFjK~77YSZ8cid&}0Dj3-z!*{#C%4envqG zef^dv7QX=qA*SRmgoHGpozP6$`Mx_tR^l*54>C$o`GyeytLnV4{kcFr^{&`L4U~(- z;J+#X;dT&gv}S+@d3^gakRjz5PQnY#Z?5j{?U{ah|I7nq9jy6^rjKvDdkDQnuV$}- zxiM0pW|tM)z7wK6Ov1Cv&yIufkixqr{gHAfHXu!7U>ki1B+7oBOFyPipcN8U7h0>U zTzWJg%AR)TfbrGJ#vWLg3%$SowhdHa>oA7`T_9n@A)?xH&1|X9R*jETKn_FSHU=fZ zrx(z;F>?%>k+mUM05gynXCbEMRe-8y z6am4++GvG{-@%45#((@{B{c{&87BZiAq%Gl6LUpqXvS(70ihuDsHy`2kp=@cQ_BPC zDhYHjX8T&04?aPmTW>ZX;O{HUgiv(kkVsSz;K;M;u;;+)XP*uk=sCxN-Ev&*f9@;4 zQ2ZYi!1T{xGIta!7L{Qd9UF^6pf8w#-qkeV8$__XkUn+Q0A93;Qw$LTv*G1{||o}bN261Q_Q{A}vYzbw3C8!qNm zxS0sT5>x1TiTcO@!7$qVLQiJmc{px>QDC#_2Qs6(UpP{o5} zuhnVO!^df;xGxJbaWWp2)#b$Wu$cj)9<@?54vGuFAd88gdDi4?&82}`f&B|3^tq)L< zV&h{lmZp7?u)aMN#vJ7PQ$BTF86f}V-Djl;b}4wVaieiI=zp9gMeIfcyl`@dfdG&$=8hF1YRdcZQz3^@$v%8VGb#aNMt}udIs|GX zA%H|-k#uO)2oyQXYTgC=GfqK41R=2?(oL$d-WoIsa+_NnEVhUse0+|KB($e^5cobC zRS>1sV@Y)npxr7ftXpszzmhcN3s!)Zb|ksj&LV;`v@S8)7@(Oo7n!w8uB=2Q8ha4} zKS;=!sq-jW2=is(N<0Amn_5{#*Y^UixM3IMZ48!V^%3#z}hZJDOGKx@a6ENO;#O+KW2{nsoptuxG~eBe1o4Gg~7pp%^nmK^pFbu z@cjL^Z~#do8&n zr=JfNcLN#)bkMp3x%MM4O+&fRfzkH{$<-=%z7%nL8|N&+_tf)spKsReS*i^6WR-p0 zUa_s#S?Kx<7k4({<>q;rWcgJiaPAglb_+aC@ZbmiPWB@k}R&dy@swS-~X ziHzQ|Vgz##iiS~4vh-VstlJ7?q@hjWZ`o$Aq7knpvB5N2?&|_u{H5SKt;v^yr}7ra z(KktI0OFLGkww1=G%e$7gy8!ff-}#S{5+?=UZkfy?R5Bx(`Z)w<4cz-K)0CbG)Dz&@(vO6-T!| z(mD0?Eq=&0_YDU$qp+v8f-^{8*eeRE=lF0gU`Z)zihqA}*Z_{J*ku(l$$JDWsZmdj zHac0ddU|i4=~vQ-tsj!=SL=xKn)?JDc*Ld~cwST=&+6(KC-sR>r`RgZ6jTr(4t-JS z{PBDq1{u!K`SDFP10^|DXAJwr4D5Xp(~2G_jHgCYZE(=pyR7HbNy+DEv!0&Whe|@^ zEy5;34h5^zqC|{a%bA?JZ2f2OQhEbM zUF!Rk?>P(fV2AMHkSC1V%xWQYDTvht*E}fPeYLrA6uXw!{eM$N4APB+yR$1BSn6B`il$IQx^?r88nrh6BdRWQi3c~6jl zApWI@4^0Q7oX;RTKX~HHKGDi>s9XY1cvWXjp;NHh;Y16g#dX{>ehL*%^j3rZr+Ex{?R6bNyB zRi9^KHv&BkmM7>|uu#onMCq96oFP|{!3OE+uX~ zGkesO)eX!Cp{>GbLQWWT@AGawNajpe?MaToxUWy15lPSR``T7=`sTPyAI*gS+Up%U zg6Za8Tmbb6O0}B9@7`?vjt7)+Nqv}cc3_h?Z(^j-Ne!4OKr`ouD-(mzVXm*rE6 zq&01%oSVp;SA}|~297hwX)6Tp+{r8g{07|%TP71*B&y&BLe9Q01m_c#&%s8E(t$~@ zYxG?l-1y|MvK}ckD?`pXM3yo_Rv)}+%q#|Z!}+#|Om}l3du9j>sZgaAhA}rZO)=x- zj}EL*9@GRyQ{J10!atrKny&g?vOWWRMXwB#||lV=et7` z+Y>meOc@wOti&mRhc79KL7)N;$LnhJKWqSpcqp|D(-PY~ZSgFb4Dxx#jb7c$4|@Vo6)m#CePV80|szx^!7|Ixe26R#dyA(@vdi#?jV+LyGcO zo&pz9v%TGYjt-r=D!P>~uq*uN4pCNB(vjFDDi z5wOZ6+I;H1MHCM|6V>m$cVr;@dF(!tYCAA_cBg=|#zY7x*9|iSWZIw0a@zVDyfKVE zOUJU+`jHz3$*yaV1aLgb`#W45M-=EXqtx)_v9yZr6GIHw-p!G4TjKdQ zE(h$J?4m~OgZlQ3xQ`*FaIOGd^#r&$pG{bY49n*DG8FVhvPmeGzd_jy z`UVKyS$YetcVKoWH+moZ0~z+_aCCbkGpbiavM0xMyQJw+4b2?m+o*5tw;6^EN`hJ@ zP063QnGLN;O8AMk=Zyrt7RzN_Z~lU?7$xuxXXG=O(ca`JzqKBqTIUNDx0mD@Im$P8 z{d$@4YUyodklZJv0}U7kf>U?iUA@cfS1-%VoR%RfSNX8H0i`=%P>Ashe~y06DAj3b zCk9mf<6CRBg9f?u&LuWcKHmtYzQ1Ezx90IQ6ny4oemYgvL#efSi6~v|Q-Zhg>j87_IiC zfcn52$aR6wi3eslgIjMZu@0HnYN`nn#bCqq3}KJ%xcZx}A<$g8F2~7tRNj4Sac-yN zT5OihD-}B2iHax+*{Uhv>Z|uIFV*rXg`U3>d$@VQu|4j?`wHnbUBmQm#7t@1=jL>; zDWiZot)Qp$M9Em2`33J>88CkE8>hvdtG(l@)O%25JY!4b5Hs_tZ*8k|! zYNi*clhSn%;c(cd*>x6W0-LDjyK6H?R4!HHI^-Kf_#YaQv!NP|WzkGV6_GHr+19_l zLMm|?8ph^shP8eua-l`dPLoYcO}}5$Sm2GgK+7$DQ|Kaq`O9)VDL<;d3ZGfACjt|y zZAt%~T|D6tZ%Q<$B_*#O67YmZY)l8X@>3Afit`+QfOP)y=?a^B0-pi3MyhyI>s>aD z!CYNq&n6Gw5}Oa)LCEV@ZTe-Mrg5vQt78H7j^%-nR(MbFM#^6qOsonaOmW5>xmBvi zNN1qlOUhg0aE5x2?2%|nIkOB-Ox&g=)zobLFm=@2KL-zcH1oJvPQ8^s9lPJKijZ_W zUMB7U`iC6U$smVOh1gp8c8B3;+i~K^B*;>Idp=at470JjCbt|p(j3n|IZH_SeN{P| z$3Q&jR{fZ-_hhYD9t~&+Q&GFy_JZ?xis;9D#=W9yu+Nq-M~1b}gpoNd<~(a1p5&99 zZ;S8_Bc+eIljMOJ%tc=Be={+WM~+uW`K*tngW=1Wz+mcOHjKY~r8AzycfHV-BRhAW z+@|T(ea?F`_Uu=wfx;hgi5=FSgRUB=uG%lgZDDty1y7!-79frPM>F0!Kpx*2LtxPe3L4vgg{|=7Li#FY8j{}xJ&iU_AJsWinrF>NW=76o!YJ*)6L8%n}trEKK zMyn$#1U*@M@AHP7I`f9(ERC$%Ma#~Bx?Ek2eN_#FXTKPow6em$+9=zI*B4yp3=MZ3j<4=9Dg+9&V73%tnw&q+YaE)HDp8c9^q3QD zef+-O`bZQ{5X>Y{m774e5m_Pa&(HsT-98dvW7x&N2i4YWN$ipy>?UUf2R&+GQ+qA3 zGWXK?+gCS$)v#Q$6d|K(k0W5M8DPLE)G0jJ5az3$H+Z-?o_6{cHNcMEt1g6yakM64A3m|;s0k_s;b>>3wUm+%tv zlat5LH(Xp@&qC+C&L^t?Q<=$9i$Q&m69b&vKcK}Abo43NST&L4^|}pq+id^0~&SvJi@ft72LHbs3X4caiGp-Q2hRSy*ZE%kjK52?-HW!@4Vkmwo?RLeo% zI6!|}5hz~o$vQta>DksP%W(l9ClXPvHx?KHY4ExH`bNu94c9-0lZ38i-JJN|omPL3 zC-E*=+m+{_XG|-=tvo3MO2_C=?@e1;gw^hQFrc2;eE3xE_l+J_SqN5ovb70lAhf%o zKE_ZJ0xh;fZcFw@w&;v=|L}oa_d)cqv2$_?Dz){-8%DysYO1NV-h85B3czKIcF=0@N^HdL3nE$LA7 zwv3IPz&gG8?M@K@ON>36r1l`WHJPCTJVQ&Zc_Dba3xW`oITW!yfErUjyC~^Adwb6i zM2hX)C}xfh6$yy0Vr}kT|efeR858SM} z3y`SBxTTrVU7;r$E$YeY<78?#csu?`8o`ZWRz^g*RjVn-&$BBpmA0{7^+NxN5|e19 z30%{Q!3v`2Hv(b*;B;Rb4*?n=bkotA@pRf*J>lU+4Mj}_Jk15iMyJ7wO=hqIlo+cQi!4|#PDOwt@Q z>-);-WS}+;R31DmH||A6d3k^omQurz7edg>(yVtuBF6V;l4s&1F9T~^AU%%q!C%AyT*X{K@py$>I3+0$@m=% zdc=aX1ly6W1@aS(diRdSM+lg0T<-ur|8Rb;Ri)DvHfjlBslXI1|b&PCVL>sg+owKWm2YtaSB3u<( z@%S7&w@=RMT;^q^K-Wh)O)x2R`L`zkoxr=-3a(Y&wDNwUz;KC-JDK5C+5>!MA+Ie?XwpQ_#Iz@5+fM?9?(eE~ zfw|q8amWMRZ<$#;!F7%}!&h8b?!Kb$4-i*Rl|?+OC@xJI9I+|U?q+kHQtzfgs&=wH zQ6P*-_N$VCxa$#MiQsmsH>!7}N(kt~L9dwyfWlC9KVS(IHK$6D^#h9<#bzy}SRW&2 ztfu%yy$%xAbvDpjRFZ{Hk}^sng}oxm@a0cIRqCw>9FPqFb5TR9ZhONFI?{+Q(4-6g zm_VC%%C?gXeZ(AW{y6S<-8p_TGEoO&1`U-;D2SLq-C{0V3nBz;yAhrn&;Q}Az#KTa zzvHwwZ*3~=+Ma%5C{;Yh840#AdUqgv%X5qo~? zK{4;{GAh*gFhQTpaB}p*5VrGFn!G;I+A!>7 zDpRQ80$l=qkJFqu+s_dad*U?f4&s@179)^G!$F@~Kd>X3PK2_6M$RweW6$-IjiD=^ z*$71-g?YMVNrNOV>>k!LhRwO;Zx$`+%_M5`S7D(A*G+|;zQN@>G{?!+3o4F!{T}YS;aT# zat9PD55;SfRUV`ZE_Uc5tb>htt;0R0nm<40c<0q08Z~)v?V>}j^w$@$GU)GGwr_$e zIMgqLG8y^~c~yV?WQ!p6qi+kkFYkZmrS2K0U{(Tq(9kx&OlKvGn#wjb5&@^&sKFXv zTg>Bf-~G~!p6&!L3T~Y|lDS#fQKcCfFJ`*0Bu=jR^JHT!UbVAt(}Yv=IgrNp@=D2g zZ7-XlZ(hY9y>1I0!lJWq`nqM!_P%=OS~NrdOON#RTj6v-3Kkz{Pb1iT=(?~TXq}N& z0qgEYv*HoUC7nOz&`PqB&r0*~po05;UAuG7yg#bCb5j?M_LTJ|a*J@K|=jIm2)^!WT5Is>A>(=@^HsJIXR-JXc`E{O?~LBSE{#KtDp=Ut7eEL^}B zaKRI``8_Cv-?84)AQnAtjF_4dPEUB8(_A0oBILR-Z(1#*Cjtgf1DqJa>i5vmjE#5a zE?ZbvmN*eLN&Lr!q+UjSgQ{%-Hq7qcgU}`adLwgm>*tTPF!UN6J~WDUvR;(8IW$sI zyeQ3Rogk7p@8(Ht#^?;ZWa@Jhe8<|9{X~LQa4QaeEBdB&BuvA=V*4E8=PI=_>lK;Vjy!L zo?}GisjZQwD+m~t7ZO%9;C3FGt?}1oKc| zPJH>XdIS>RhFN-RuW-P4AHSKq0wRWb!c%F&AM9e}zG@rFnT2Q%W`IIKbnDZrbC&w| zJ3Khl_&)F*Y>yl^`7TY=UX%Q~FkLNV$e(d?NAUTF+7DTf178LOs9M@joYAHGmt(~Z9_U4uxx8Ajc5HQg)%I(cQa;!T- z(8B-RhPpD&25Q%O){n2)$Q}b9*Ax7;2fc%@DNWl!#K8C@a;MzE^6N4ShMAP^v&x`j zgT`kfbkfbb0GF9kr0dpl#Z|*=?w(KxtQlM^!8NEE(!;Ar zEo|eEo!LGfjx6sPw%A{}6`pSrS6@@?OY%NF;q+QHpIzBlbcI7`9~XAMCunE(P@}%> zlOg&vBz!%i$b0igE=~bdd`x20QO#qysAWlpMAqAZ(S6iu#BuhnZ@reh2Hh@CMlYQA z>vc!TL?x9}p!+${qZLIWI1oeN@lX%^zY_$WOu&4uK9a@t+{o;s3vODe+>u~pHyIwK zwgODjA3riHp?{MV=EofCGJvJnyzz)9?~2YLrZdrg?gW(xGzk39XIbTT=e~$clH=e{ujCz_Rs#D_HirM)OuWpqfObfRF=Ay>S z1oEa+1)nqoV^DUuL@SICFTYtmyDU8YM28kjkJ}Sx?VW@H+1|%o= z_eIC4ZB$2%N+}uGJorx8Qny-}A4^ zwTW!VC_!X3wZMAUn`Z^+dSe|(9yS?O*h`eH9l@8_La6#!G6zNl39PxBW1D=RKKkXg+NOb z7%2Cw&&04P0)=mL*f==0y8P!y1U@8ES3^$IqRz>R@*4&|A?I{Q)*FA^|KpwL@q#OV z8xKU_#u?61#D?9^`MpQvvpv*O^my)IQy^(TlZ4jM&+b|Bny*3u&msuX(n`)j?Px{$ zq^?-brA%^o{q#rPo%`EvHiV$XBy?rDD*t#VdI8iZT-0V)nSwD9KFZTcstBhG`!4qw zdCD&TekJNFREOk4-duD(2Ro4JzUyNDyrOIoq2XeRI?>kGBnHJ70>QDD&@8mgGklz;8K?xW)!JN}Cg=C5-=!a*5PW{XXCGfc6k*-3=TQA)f?IGX zAce<5GeP#|k!3Kv`15n?LJ%9w zJh8?{f@8Os7|PUtALjD{Cdi9kE(1^hyjnpYUSY2@`zR?M{Z)7?ch4B5{l3CPZQ(O4 zeRoGaek_Y8s&y>pDZ6P`1Gk81G1)tVrX9 zpjX_#xB#$|k$jr|xx@eYFqv1e&$iSs-~40A2x`Di+}AkYNt6w%nEiE2GvD8yx)&}_ z7#|hm{&RuFQp5L_FcOw2!YFf|Ltb==hZcV1ur8-#ULG2?7KqIq^i}39uLzi5b?cfb zwviX%9y*!7Z#-dDp7wUlXXkOv%)PZ@`Vq;AFA{S#qI1PWd*2B}O?QFPZf+G_)Yj6aOj5GQ~BpYX4?4M!u>_Nv`j>Wj%-e)b9_%$zv6p4X}fzt6OX%bg& z`HoykyfVD~xW+8C!%tYFhIFWxo>HqpL4R%`TgUzPlz_rA^lDF5Mo24&#rD^`PYX%zP0dF#ODce)S3K>U}NWs(N zfzxD1jz&5|)+j#ST*?0L6H7buHcktZw;fCy*1`y#OfHzYDd;h`|1n?bVXgFfTps1X zdvXExS^6UnjQ+Zb-+yaYg1y>~^zrG3b+y=t_1N#5@NdhYjuIPCMZ|+hH*-ZDCkaaTlss|NX&LDquym zlf|ayD4^g5L!L^-{QIg;cmx+pFSB!SD?*E1{C z{<{0$f76bI&pTuHHd~<*JD9#cqtgF=hnx@K!aw7LlN9iJ=w9(${M+~a+9LuJU`xn8 z_@ly;h!T#oi?>rY|9wR(NWpSXn7QDV!EFqqJ*{=*$gj`%YXGsoO)_KSOuQd2WrOq+ z5L21_ple36L8jXOxQ96GM<TCxIXIA9dhI- zJ_Zs~5zVS5Y6IN9rQg65xUo}3Elj!Ms#sqR$W6^0U(Tm{H#>%|hparc|Mm*+uOtzW5!%Eq z(dAoUK$x3v3+fM20A6kYnf|P(-mNjW2HBlaiZbh#6Swk~^%GxO0dvv7Es9cpQ_diaeth0b5?-a&bp_^>{o zVq362?!6WQl3~nLLs-j16tlcsHN1f-cqK9Z{?Ky7jY&{HaT(^z)brc|A}F0k^}RbE z{Dr~}sg0GpZ_IDLJ$+NpHviT2Oy>ckTOlx~SbB;>Sj-=|_21dNkdy;#+e>&*$i^^- zF*>W1!>F3^#KKPj0QPjJs@7_9OQTATXMMPH?$;>%{@w$JaV;OXIeUETl1H!QOJgSd zZ<2dCNc%l+liMq|@K|}JGZojNByoFiKFNJ5@tQv7abkKxviqHrr$Xbk=osEgd55m| zulol_-5R&1Q;KmQ-yU0AD{Nt!sShM5C2p8V`|KSQpIg8LS+>IyLFZY2dc&J$nsDeQ zrSPi-CLpq95h%gT{hVcV?lFC1^D>00pH|srSJN-B2CVckV1ISW5E3-E@Ka|HrmZlZ z$pd#Qz$C*=R}Vw#T5&Id&N2yAN|M!)2ipQ5KZjT$cPeTUvCk zwRA*d@wneO{6ocD3Zt=K#TDA^Ue~EgTZ1s!os^5pw;J#Ihbc2Zkn`D^^2gzm+8(tv zET3OpY>=2ZI5p~}wj{VC6W`#wqvgp}UQ;~uL?raqxC7mAr#2OTg}Lj3pxKo{hg4E0 z>fqSWt%KJ%M6)Mjw^_1l9z5eiw55OVQ6xK-lkR2nAVP8qLs1W9Xr?$8cHMFYhjJ97 z=j`37gUxO-c;nuhFQGMQ0Q8Gx!{_Lfup2B?h^;@QtHpfARz9g&BX7keuuFPQ-#LYI zq$BZ~AD#H{jYsS7$1SVX3qcVd`@UvdCdKtNOWpmK^FpDCkkPku(6Ke^uqHqz^N?if zVW9m(#fh2bP`)yHW*uXvC(jI(d^QavXEWWW0tuePWVJ*Hd?PQNC{=vIkQ+jiPWe{S zR(ik{7)>ePcW1K3q^@=ROJ`dV~2 zavlhAESHWX=x~NH_w%RuZYK#Ujy!)BjnAhUEnRFBtZaSfBuB`|>Ml>3o8^e(zyr4! z)r%8BkT#gRK-2U+;8HWS2t(qdB{B43&)>8S`8Cs_GP4Rb>_&hPNWTh|)xwYW4a8|N zf=^G6-RKf(VJrexcKi4JHRhc=EMY9!mRTJIFsGltS#iPnmm?{#ol+$z*1*Bx#JI=o8=pJRw4~dL#U={ncM!D#3d|oF9#)^;_-0y;#wf!OD-2Spo^KI zQJ#|BmopJaeFvnMpk@oqO=Bx0`l&+MrM&on+00w_@~)MUBU^3Rp1x>=Yd8Pu*unUfI&zH*z0}J%Qza+KzoP` z$6AIm0v9NG@5XE4ZkbIC$h<84lyXTcJ02EJxZ$D1s+zs#lC7@u&_w*pZWw?ghDUwX z{Ni%)ryyeGuyF&o+xen#7j*~pUW+p`^VgWW>Up&#n-RwjHzQ2xDoPQm{mMx6fG%_O zgm5W8muqCK(XA6S(Q@u~qZwLqZSuIL-X!RQJWlOU9BWc^amBOQYxb``)I(D)I-Z>{ z=($prI1Hg#)MQaY^jipe9GmH^8UtpYK8g;neZhk;?Qwk~H5;oqEy;?Ds#-2q z^VppV6X`Iz`T7fvnZtW3Ob<8PX-U&&bKclx7WVylTvVc=vnq-BC!V&{b-lUz~Q1+aY3RN1mQ?>guPh#4F0j4kz9B>sqYyDFKE@%dch+^^28DR zL%FRUMMa48W)!BORf1946OaxSQMDZ*U_@3WohOxLYGbf2PuP8Z7 zC+TSNiW+rLFlVx8x8^=#PxDr8e_61BM8v#w33g>-h!IR*z09d$!PI?(mC~)QRTsDH z9jXvE@c?m75 zI`a!6IQhk^etjWzt~pxN%Pw&P5*~LbEjW&Q&WAt}FR}%$QSY8&2;HcTqKj*veLV?Lf0^# zp*3c7C9&dE*oj+=G;dGuOweGCp)dMOms=BwDhHO}p>Syjgy8)U9Jh9wmlR^TC^Ls6(6w1H)~AEfrITEdhG^`n+hXrY(pF=aLRWl~ zd8-3{E)a8)k%Y@Z{o$ETNB7=0u$(sQNH8SgyMnN?fvzpk@QW?-#A$Is>hl*DG;Q}j zCX)Gsz#RHBUig*m{Vf9wtOs0#$i;;p?;>9hV^Hknqm@qo7McM5C=O_Xg$w5xIo@|F z00e%PE&lscpFkDDg9E#l!Sx>6V<-Q6=3xJbQw5Sv7ICYKEMsutqMV$=gMZy=i4rv7 zLf8HR)xbC`1QIz+^}oPLfE_OS1&qqz5dmVsb+*g%Z;$$)_mjzp1u{0CR%%gU5&4ar zfIt5Ik=J9e^}Zk+3r@H!l;DHW|G)@>uw!`Lg#)h_3IcJfGc*1F;|>?$!t+)uDU_Va z0D-hUHTt(u8*rj*L%@PQ60~+*%0vR#gMOc;{GYr3+km$NSe4Bf^L+pE1`HyVKM~?r zEcuToI>f#d?*B(G1qxtT=*{7evfuEyzum^J@?@IJTf(O2ealdwGE|EIrkM^`0k#qZ z~f z4?bW?$dHmVlFF>t?H)q=b6MV+-D%Syi^P1l6W9c6ccX>5!7|mpe^NZnxt(ju|8W1B zrF3ZqmTmFj^jcOJ6FoLT!%tkpg?-r=$A^Fq{u5~3rJgH;z^Z5fssR(VvBy;PoXd6W z?i*N%_Z<>_U_P8~+Q$ZuB392Ka^~rTKxr!P<=b{orc;-HY%IT37b$sTn^`dDvyPHV zvK+vlPXWpC7&McL*xz}3dJO|uC2w9~J<$~8d_>r24GV>RIwts(a~KS#8UT7c!J(N( zYYafndLxBaWB4v`g9Mf#IW&c_jFF$X@hGa^spW)j;bReTUIj!k7A1wRkOgp!Kd}8! zp!T46I1!YC=&DLE5q8Y8>idQbqNqA6si z-3N!QY%wGw4psJg7Wb}zdn>qWIKn@K0U=!e`ysqsQXnl|%clyLLB0SOwkXaSBK>rm zZ+ZLu1~;SRvAt`2JS@GTP3P(5Ld6@vZI#iD57&I#0c4Hx;bO_Mp~U6lR@Mm2kY#GV zsa_6@ZzOPSWRU4TpWA4SomVD6EpPVmeaU@*UU`knz>ZjF?O{37`h$y`n?CUDEo-l` zL5?M*?Su_ULTq2W77s1jOQ>3{%>*$FH>-ted_ninZ54!MQo4bM z4iU2eF(v7U-AfH%bR84f?>-p1*>Vv(aMzmJZxC%_**Z=`r&r6(Bq5uq2Y8a+U-{&o zBAG1Z)3jnrt$@M+lOTb6DfuqN17BghLMXIkghm1R6vEAYajpr=jaB4*n+hHxG7wol z(GQsc^?rSv8b2K{`l)zM2teGXz!QT0~;Os7n%%4-emikj+4L^@-WqcX_T){m2QQ{;8CWYeYHc6j(B zE|P*Hd^TW~ok&yy8xQZEG&;oxmIEJxkmKvG0jrrwJ&;kkpxT+bqe^oX3|^wB#;{1a zp6ADVwz8DfU24R4p}qQX-fZ??T!7{*WF`#$ZBb{JLL>wEX_h*;gpdXaQeK292W*&Tq9wSJiH0vap6WY zL(67pKbqIJY|K4kiB)fgu>xu&*J%1B`M ziLu!gc*8}k(cMxR-ue#Y)t_b0)OkXR({w)=iS z6lAbpkgmR%*I4T}AE)yW!{+~S+ISym5te#;3>%fk~3Jzy7Ej20Q`yipcVyB`0$5^fZp(Gh!Kt*Xc zU}muF?|Zp6bX5DCVqcp4nvRRAo^^(lrrGfuFe?#9@kwqAv=w%#E}7L3Lwop5+E$2` zwyT}(f5d*2Sn{l1F~4pg2c?P`GS2Zm33~VcVDCMHqT0HxVMUO(L20sxBn8Q!a0p6n za#Dl_Bq}0Va#Tc869h#B0Z9^-B#21PK|nzyDLEraB!fs0-nq|tPI|7V>V8#U)pOr^ z?;om_#qPED3VY2l#~AbckB!wcboPU*f&64D{Q-H8GjZ9u9oarXaq#vMuWoBJFau*l z^%swypW22LO{jg)ibC}>Q3Ccv7tOCtM9%d`%0*1?92+4_j~C#gz2*E+#v>jBqqXow3xzy(Zg7fGnNzGcu?d@?qPB^7*`1p*+P>TlYHy zXa;NJ{lBu)xO6JyH#^uZ0>;|^@(e*7oGA#t&e69Jx=7w0UM z>)lr0JAAyX*yt(#+(I(1iYL;7e35tSm=c*eM+1xv~{5yV_@*889+{V@A^f}pNP~*fi(}vD%)ZF}Y zXMCq9IvhsM`Aoz4hpD0=Rh5tNJuo91zaL1Xl8nUVX{NLc-sfor0^?B<4VFfWh>4mH4UKV|P=TQT|( z5xn$d+Yh7vgf+khLv2yy`ar%l99H+)iz=lJ#SZG@`j4rKV{B0N+*;o+-nbRGbu@|l zVhThxv|}TI;l9{jt-^0p^3mmf7y`0#j$GRp$Na7@j+X|t+Z|Ne%qbiiBil4Dr25=Q z$0gtqIL3Hi=T@Z;Hb#F)YV|s4cUQJ4)Gt^Gpngh%!hVIL-tHvKM00xI?;i|U(;)>qU#Qw&y6pr7&^sl^7QLm%Oase5EX~tfh4K&XjOeY z#z8|_zEAlXbscNe)qdWJ+3b*LmO?et3ut=+$^yYXDtnUd3y<2(+JM~eGusk?b=SFn z2}`Tp-E|6&7Av3FZuj1!gaqt$vvD{J(#v}0X%UvWu*=e35h7M)U!K}|RFaBJdg%ee zyMTT4or=lJnZ-W#bXqJaeR}RW6aPJo07l6{>TS{e7=4r+>dujqvmZiYR7>3`k z5RO;jA31DV{rSNdj;TJIKs7aR4Uj2S2IB@gE3uH-+Km_YwPXGU=JgCO?khw~$-^bIu2 z;TcA<2`a`fw`}5dI4Yw)9FpqRvQj=9fp>eKL=|1To5*v!|df18kz?s z8w$TnlqV5J-|5-f)mmvZXnt43;@R+>QArhp4VU2-=K4Ha@pOS5rHrO_fz5J?o;nD} z(S;7Uj?wJuZi@l}CnLd7TK9EZKpyXJZVa(I)--z151BgF)HI#2d9x#b-h+CYKV`@HU|#X4Z$44a&bU`jlP) zNE1R97XMs21%YHU->Q@!dN;z(sNo@++DciEW;Bne%Z!}!For=C^B`WrtY^EnK`6#%*O7 zZ$xSWY}_C?h#fbHW)9HdQfqHG7l3&H*~yOSx&*tD*^9n;6r%jENoM$Gt_pat53geG zEYqAO+u;$b&@fTOXua;>3M^@Z$se9{0E$ZzPO2M8${N~R7RfBD)V5eepYtu1T>eqJ zAAbSG(#L%NS=6a0OWYXwnC5wB&0dbhOeU=3Q83kUPc3KJ%u z2Gd?Hy{dwyK1xVh*mvzjUg$g3Q>1nu+Dk>QtFbOJaJnR_%o~-9JtI>3w(8-?W#w8g zak5nvJ^YFyY)x=)*|4@KO2?h*DSzq1)@s_c#v|>_g;e8gPj2iA=d}~|D4ukbyQyB^ zXOEZoou6X42XI!~{_%2Rv>ID@G4mT)+z5e-YAwAxDJr2bJy}~x)6z<26Ia<7Bi_+N ziuHhl$bnm?;mjiLbB8-sup+YZNm8)EH@?dlXaP7H&86g!P^Eq@~wCRH4{o{|0 z$a$=V)3Dq>*I*GqJPq{#nxd6SA79i(;|xKa@f==FMqVpA*KGrQpBzyvFi8C9`qA+^ z19b4Mk~NQd)xmTp34D{VY-9Z434=79$l8i zFJ@&XE;3E=ACyaUaTA@?I&EFxp{ZuZ5*5_%H6qwCoq0L!dcakIWiO*Eg+s|w)=ewP zQZwoIA-Hi1ioQ=Q%VsI?jkG)e4m#=?PHwg022bo)*UP>txe&BM5HHJoqm?y1@JBt!8Ot6I~L8&0My0xthm|#jYj(izL#u=@aT^H zFOTkzC-MgX{6B<8_a7rS0ziFxbEIIT%^GygxSE&Q*)5*B)_m-vX6d)YYTvqhKe5nP zvtG;mxRBYJs_`(c`An~=!Tp7|BQzzZEoSVFA3miOx9d9{Y_*`fb#=YKoTI1g(UNc( z!>7jex5K9Tc^MCGJ&kq}`%>YSK6gS>C30I*@R{(t%}S@+e`GX#S1C$op4NdMMzwr@ z7R34LS>6b%Z%-&$zMqvgTiOgoC5Gbckxvr{3-iqSIR*3?73d~7i;Q9j*bt!$d121K zt?hpS-@YFa+=jQ*(T`K$O-ILwb65U(Gc?k#;I>#Qr4ndZ{_vr}-;@-;fA3#_|KuBRWwtao`9FX6 z*H7V2u>2o0F^gRC#Uja*%g+B|{QrB#hC>k=I*Dg5YBj>m<90cjfBp>7yQe|hl$9*~ zXUgEG>4IctxSn5Mg0k;40QmlM&tY#nr3qvDW* zhm8@M`G?VSLb&zKXFwZ@Fc_@PdSG2k<}YZ=V^ueC_ppXa5e3cn+Tei->E$ z$;6v`v2(z^+57I?0?_Vpkb8xFUjjw>;SVnX)ei&)81K!pspyS*hTF3#{!4IOQyFWmMiP3>d#`$nVmj>$`?Ec&r?jLAJi!BstRoV|aw@Wxj_d{pv5xOym zdtW5XIAZ_RROuzmh1(Aw7(AD*Bd{XHB58$VqGLYG09FS9j4V=U)62Iw3V8d;X^;_~ z?N$?P+?V>+XKDhcWKu}lU-I9Pvi_La0cJ+a0KNtRPnh$f6wn5)tW-|N*#sgSUW6j> zYZAe~)yc1(2z3@=&UGM%37~%=p!#?FyBA9@zkUILo^QB6akm6;jTmM90)%?5oR-YY zE9_Enj79?FO1M!IK*Lb(Lyn5O{h*|yLcnN*Yw>1Z3OHEf&{0xCdO~I-%MHwDHV~x# zphB4e#2Q;_3kLPa0DiGQb7OMoxbV#@<`Ph%B6NS+&}pkL2q?P)f5oj{t6XEVv7V zw0Z!cS1{>8{lj2qE{(eccbZ_Jw-672=5}DlFfqEXuD0@%0fy|g`@$kdm9LD|!Bz#` zOZr?D-5Uz#HfDK3y9qY$u_U}yF-ecXl{^TtLKpTnVX{B$JuF#M^kWZb+sE$gUlbX3 z@s&;*D*~$tSH}B5>J6;&=q^H%>y5{~b`- zF&}a|QKPd51 z0fE8w)#}c2DZn>KzKDkVb%8%-q~H{3N{5P1$l7PyJ9nFQ`>C<-jsaVJ<=m_&Oc+6# zw^kX6G`;dulu_5jYdfspXo??9fD}q!J}e4ig0Qr(3tW`=7X>X`u{xA^`ohK1{k@&Z zb1B-y1R0@Y;TSW@WPht4$wJVsA#U{Em!6zu(Fw5wm^n6NKwY?>_2~>1mwVPS z>w!whA7yJ#cco6PcVBv#L>EdB#Zx@#OgkJ<_KVFbt5$z$im7Q{5uZ{yz3v!-T1?)+(DZlNDAPsb@;dGpA4+Vaf3tbxQfKKnUM_^zQ0AcGKWm zgLd(^V5KFwS83jRMOoKc$Ul4=kRtQ1z;=5@M9v#!2Yc|@AM4$^IcjEukRdq6iI&tf@!snjd&;Wr`*X zk+nmMqG@e zWy*VDN(HS53lssJfA}wnu6F;TEC#@diOOy!0iCYH{6I#@Px~sSo_-e>XQ3-8$i&iY zos+B67LVrU`fw}qMILiHLbWqf>dIBNCc7dyn!oKA9%p2_!n_5k%{M{U8L!WwLznSE z*G3{>b9BA)WCZ?xqLf+uiPP6x{$dsJ@izsqNBxAWJ3Q#ao&6m}zF3oEr#N!&dF;gH z6?lwI`zDk$Mv64)Ns@KCOg0@7v*)G&R_})kC>KjkED@J?A1&9pBZ8>i*;7Famzq#1 zB>7+|!BVaF!C!=IztZ($tsZYstkCHldhm z&j^vbS-45T7K{6K>j=}W)H08u`J}TNsUpIn_0sa3CV3ibZ$*% z>Evx4gYk+*sCn_QjwE1Fx3pRVWt??C&Fn~SzvX!a-H2a@m6rg?a^GZWF1lxDRc73& zFEUsC5}2koti2NT#>PGqPan*)?oZ!--{)lb5P04b$1gf~H*Q4C^YU7jeE>jzl%$gN zH6+Oc5jlr3ciGbCr{NaFdG88USeBo&9T`-{U|Y=yECJAeN_pi>u0G1Kl*tqTeNVM& z3jp+;dde7nTe52LV1ZJ&b-siki^?)g21Ujp{shqq9gWj;lzV+kpYm*;tgXyj&Wr?# z=A^NM&4A&vfC3Y`Lc+TErMdv#z9T6Op~&4bwS!KdtW7OiEOXls^9K zHIJ@mM~yT(1@L(!1^(y{2mJ^R-^c)&hDpCmM#JFeN|QuV=??~eApFrgDHTZU%IKyi zVo$yLP1rlaIglBz!&KSk0TmxC82yfjSPs|G4;1z&0r4|{xD~Lh6{!fBj(%auNZ5(( zPVNMj&_$Mf-0DYcDMnpxFhKTTKZ79IT!gGqB970<*X77E-4t2U+<*!!(>j82t^gh8 z!f(l~We8Q^ZZD>qqPn|?CLly)#C#9l9Q8<=uA;aFN}}}=_PlfTxz+#eE1zz`W{P=e zIAk#RK+tqf=gX@qLs}25*!h#a4E31bvo)rrK^YwzRQ$0H3@>uzQNGPUL@=1Y!f=1M zu8PdGjqTvM)+ytpX!>22+Od(ac@b(la1RXQFL40}_i6;*EvPWD-rSP+H8SO$tH>zbzD@E@FXGoNvVLP{Bkr>%-9G4Zr}(?b zI1yQ^_*cq~h81h}0BBjn5R~SVU8i(p*>nno?FjkV>i0YTc+P6H=)|PhT9Hm-l-Ei%&GID0Oq4&P zBavp)ijd{zq26272D1a9SXCqa?@mk#HW;k)qKTEzTdya)*pNG?%KoL_S3#(5rz~~T zq&bPYj^md#~5iy^{I~1K#28ibHGn`Aa#!a{+F9q*cHQ0X__O z;8|FM^XXCO8I}~1l#^@SUH-(ItSFYxDH~{`yvyfX+}1kON=R{rXMSi<;?L%(yT$0> zCRXV(c~L3XqeQ4*wc3%_m-jJpQS+d2*$0N>mSdkq@QN9Je%&_1H_D7(j%WeE8EurR zaZpiSAN(9@0eVn@rSC1{>GTv+9Rp0nd)c!sqRRLE z8-*UJNb1X;2{>@!NF2TX!x}{L7s662aiLev_|Eu9{5;L3qKi$7D(?_+7ZF#lwf?)` z(+2AZD<&rX3Jvf1*?|z5_qRF&dMbvr&Phx@KR0rMce+$DEtIGHxLK7;fC_DPTi%$P$Y=%s#U)3j&PmwI=m61So=gH}WR}_3;|_;wp>OL#fMiQ=g&j z&DaA(=x)7!G(|DS>U&ZugPLOI?95e7PURReTxt&w?sh?qa$QIXwVp89#QT{00kSnc zcz^tP8X_(uA3OE$cPL$6LI`l~E%R}<@G(KPR?c#@l#T-bc4}`FT_oSP?+Fx!nwJ%p zW2v~>W=}j^78XN|Z@y!$edo@VWFwhFnC()B%~yV=-=%rF7haykqgwbp9BDzWp)LwP zCzhll%{4JQ@(?YGjtX^&Z~%6srjqF8hP}9vvBi)XtS{iXgaEmuG^cvt&FSRb*}GSZGN>R!90XLh8suniTB0 zTW_b@Y_LbUB z=Q#ruer{4umLlU8aN>?t%iVSfL0O2fCspkJ+F6y(*OhT=OaW@c^m21BH!{l^3Hm}mLp zqq*EdKOIlyL%i4ijtFuY0bJMBeqghP2jPRS=9g>z6{7r;NUh-mJwIsqAys++VcFfq z4@v&}wSK!a2Xc#9=|&F;*buCbr8wsw2+%kL$lYEpbwpDk;Np{b=YJqTZy}8L(Q~d3 zsZf9k0^9S?Uj60M|9TDlkMfmcth-mWwWCU0+NM5N-2cU%`1sZ$ z*_v9YvMpm&IfJVn)tAd_eV=dN41LAWKRDs0R`!+kL)q<*-}F@Zu5Iz0UJ5ntA#J=M zvO0XGB(3}jW0|K~>qgE^;GxbZ?;4TU=AcF8vMWT{ljvRfqoIaIv+_e2Zm3|n=6j<|F-7;_0MD%AfCAx@zcxwDU1k*{Aaie6VpG5FjUy#i!a#prc1wrE2}6h zg#7+hf4?K1OY%KrAnO^|Ji-OR0)HIQy#eCj1T}Dv%YIdANcv`eFrbG zH(LLf3<~1i0WhrV*d_2feSSgVcS^s_4;il_4pDLLbpC8=XrP`&V$p={@6*cnIfRT>7d(4kNOuxKI02UT;yO+U)BAUpe(TqL zLHdTq))c~9~dDTc0+MB?dss0??bEOWY+;$tU2z6zB=DojX z0+tJ^{5ua?1mi2M)-HV6z=xxi!7bR2m4Mm%Zh;|Ut~knBegln+%as)ND*isQvvn#dh!+9kY- zx5Y{G0nOUcE9uUvvLUhAuVRc6m~w06;0bPn6xbE;tw)IbHL6G?whS{l0B~>-Fx2c^ zxquXxYZ|q!+UQxiAYeCCiYWcf-J<6k5$XTSLy$tUA+^vu=6k-Eo8WOeQYh0gF1=&b z7AW0EanqNLAt>)Cu^;sqo(zmz1(8SqIJ}4=?g=ZKcdou#K+ccJhD09y_h^kHXtny< zL7Ck+zXHzw0u{EQ;Z72VhWLNmkK#a`r?hrn$1aQDJV`n)%A{c*CgYVY^-U#+U!%rn z65WmOTsv{z>BdclaHC9*3107VF@0dePrgJ1!dH~50f1`S*ShBlP*pRUd*nkRFdGlq z|L8i}3rHXn_j8^`%H+@jD0+A%(4dl2{n;d}6>>y!-gn9$Ei=+`1{G_QS5^;&7gl_GY6tl z5T(kU?s(^5;#JVHzV> z#3pL=GLV~>CI=c_(;@rh7-9NQLfCD_13W%-Y|dl9V>wh)%;@VRjD+;zt>EiK#jGUx zB{S`MPGvfV!ui3}6m7ZM@Qp}y0sd!?(|mV+&v3WN!b|biN%j&=-%4b2VHML5M5UyL zl$aseQqhZq)VTZ9kBs!x!(V}Z#f;Qr^8VD>9|rbS)CSBNEE}-h9WL)r4UI%dRgN4H zIvlqSUT`!fAT&VW7iv6WNJNUYoRV#1l|eO!O#=GU%6J!9SB-hEGw+MfrAK0SzAa38 zuvGfCor?#7#iTGMdKps%iKL4~>L=~Xjjw=6#`L&CC?>j27*puLhe`bkY0Oubtg%ss z6~?V3wfCqsk~0Z5h*%5_W;GwqlN_|9(9D++GQfAl`AZ4;WAq=`B0?AOlmY$JS3Kn& zdX^~x6C13Yh`_7zo8vGdr57;TF*990{EJNfq&D>imE$;?ql1v3u;isH z0#UqsA{Lj>ogo%=72JA{fm;Cz5#WDiyjLAGcRe)kGD>BZYyNq>U5F8@UI@4J<}Z*- zbwM87+T=qibDi%9Fdw2)UYNq~%|*FI*BnATg-D2sNA#?-p4B7DlKr+@GEkH2lWwy} zkqu1~@t8?GGrq$goUZAmlXh|DPDAGvX9a?2&Zs51JkmBi5(C=J2b>y^C%ApQ)j!(L zrCpIEpVg2Jl@eObI;2$11VmCZNNH|(EF`N+KF`hZROopHIpO9G$+-)m=cG%UI#eFm z3?QCOh(6o8Lj4(2uZTX^Z?lgwd8Y<(t{Pcdi6H6y2$}vSo726Lc}10^4PF;=A?km; zX#I~*aQwk5h|&4N$gwH)@qC5W$AS~vNAI*qddSeJ6wsA=ZdI|l4&QCu%Z6}`l1I3$(Z{;i8 zzFx^9xrBQ&)g{z}g_K%SID1G>BpS4`-Fhk29`NyTpOOn?lRiRMppd;&Pq9+7v{UtyvuKXaWY7)TXwsy}7Dy*{R=PjVmfYP*nc3~d80b}+V9MqYT>tWy(@ z_?gJx)=CL@RwI+W1?Jh2#k~?z>p)K47c&ZirjzuRSOJPe&5%D<9A=++v zYI_R{VivWFQ5GC*tL-0(H>8yL3^28yT& z(&8tCjPGlGW^isR(RKoMgimxoQ@ENAB z$e$YN_`@ptmuK*<)%sFlM-;*eIpTDsin;4rtkMC@QI8Tl*J;&tg_ToAcj3^*aV$c) zSKEcU!N9Nbyt?oIooLb; z*jsS#g&c_1q3<^X5<_r`m?@d#0(g%!%@zklGYXOPQk36R^E-+ifdZ0EXW2OMYlb$n znxQs^ZYgFbd&Pc-_kQ2Ee>X1GNw$PINIuQA?kK9m=o8RObZoiwZ9B40SG49BGS9h# zpH3v0m+0bE6zY$0$z}!b!l@q%W_N;<^*7v87!+x)%pRZXA~dGcjb zTU+}UQ%qdzJ9j#(kz;39hnc2hX(9*2xm?A*6mWb@n|}3fqmRZ^te~WE;fYhfa)R?l z=ECb}!`f9ng%R4S7jz-6cAqQM?G#I6#<3GkMxsq_P%)((7JWejVWrKx^))yu)?0@2Y)bg_My8tc?I>d54#I-Z?e%hq* zKiIT#5<$Ia+U@l@t;gCY@Kgbx0=PQzVQXqoKm=O9AHM$jDT)Q~5xVC7suze12Q4Fi9e|9 z2w292L>&TQuRZll;O42_YKM(}4m@RQA!wlNe*z730Gn4lQgXtKn}Mf_;{_kiHxSV| z2s@$wp|7OKCNKJ7(JTPj!Kt)lnigk$10uhpC0^l`{lZ-%F?SP)6Y)h7NT^u@CNo4Mlya0XT>h`k5zuHD1`Z9?kvY?5Bb-fVmtX zsLohAQ2GY}TsoHIj(dNjN&Wth6Vp;FAI*z0(nh^Sc3oQ4O@_!ugTA%8kQqVg@;jCL z#<5;cb~;XDgQl`a-S;-8=f|7rUy?a9-MR9_WL(~9VD$1*C4;`}EM|Zw|JucFv46Sf zL?O8pE5)H7xhUUXfU`wOe9}Ss#n9c&VfRB(U5^1S5{3#bmwwBX;wY9YwEK0l1 z|5*F-T-7E8^W%ev<0c?Vi=zljNyHr@ z^|Fdf;5}gugn|+@HCJh^>b_-l)fhz)v?`?}~4P-3;u9Jt!Ln)Cy(Wsi`mSgOI8WOQ+_(^dVccz;!~W#=i{ zZ$p5rid_bPXT-WUFY!I+*bs_Pe+;T=6@m}I2c4J3wi3739g^s*(Iy*ZY0`*^xLXTn z1{L8`df}V-Feg9el}?~@G@pft-GzN#*sO(03;+Qh5s}`fz}m#x5i_`i&wB$R`+Th1 z1|Y0^P=3Z@(OJ4?Y5PUzOiNyzWJa8j%`}0@%PQclF18*3U{EA6DDVIQiW!qs`$j6! z+Q7-_#eB?Y<7N{p69UNG94s%2?k`tQVZJe+tzB%=(+{PvArcqYui zHYwxj>o_m6dvjWHWAD1Wf$-(8IaoF zy0p2hO!m5xS&l3eWV2SA`PRDXOZHG?w668mty_yG(Od^Veh^<%69eRS0h~#Sfq&5| zvHoSaqQo;7YLfA>_8aAEp0PtS@vp-AE{3?aBY+0V#k}&AF0BZ`9GwE(SWw~$&SM1v zB|Qa&z(jaR0e%>UjG*{(DJa=CJFP>8sXCguzO61Y{MnC+hy@7B724UjciQnhdIfJJ z&gSa9elB#guS0@RdlnGJhv$zFIe}$f3VjJ}r5t6Jiri+_;_Ln08L7o*uq_P-xE($5 zOP{iqX>hfo*XQRpge?g^SPT*6_MOh9b=ZHd0XIBoM@%{;fU7-BbOL2i>X4EnsU;ta zbs&n}uB!Z!oijA!KL8}|n{d+i-7&n(kuvHXkYa2zP?#kagf;Xs&*XG?Aa{!ItM>O4 zX}RXC(Nx-JWC*MyzHs9BDOaRD>ngpIQPMzsv(9#^>z(j_9LfD;=JUlme5V_J+?=c|E50>Hu{q;J7nQ3&!?1sJ ztSGmvtk|kfJZIpQR$aTWT9UazZ{DkHh6@AbE0ZrYtG0E@^wyr7sofB<&(pT+Z)w?I zYBd<4*B*YGtsps76;q1(7KR}h!mHoh0zo4&pW=e14hv)9nc1CG$#*d9tIRlPY zZCtjrbyCcEbGqD`X=`=7w#w9NR=cn=Z@)dDAM&W8S(y8wmN&(v!;`t&S9q;DM>zIw z?c!sN}VFmKN+tekQYZ|+7! zo$6$oMTT+PM+30mfaqNAV5&>aGUB=6vjNVC`7o&m&EG${oOy|em)D?xP{wM+IU(mO zDl=O)i9CDS8esMmlHR2>zmJeucsVw+U4fMe22TM7H{d0La%4~~Dlq%_Wu=@0IGMLY zWzNH+{7^`j_v6dG>X2jDfnr-5FmefVT|BvSh>eJ6TWu|RQfn$Co8}W$)9ezD*Lfdg ze@V~riM}1@IXc`kUR3fzzt?WDeY^i|n)j>RvK-qljkJ9|pK=T_$@%FETJ3seEl~^2 zVPe%oxa!-kIo{vOif6M@dR9kRbmsF<-`u8->DyMOd*GPYfHlMI(zcyJ ziQ>;u3`wzc>T&upmuh&f#ozkzDO&LB>CGi->yL#r{y|szHa2uy8HDPy7Y6%#cGs_N z-p<&R9GMi*60|REa+2K3PxgL!N;akOMLcnTagoiCjq9F6NKxg|!PXz+V(}Noup%Em zjoz6uZmk~U@A*6sEcljT#Z#!jILGyEO3$}@7Q>E>L@e|c?FEkPd&XHqi_dgP>dyp- zb@MTe)t?sPjs3K8b*ZnOovEa;T}o?KM{`3tV>9U4{6b?XIWh||%kt7!Rk~w_b}dI@ z++@ccL`NS|cCZtmsQAM}WP(MyQe}b}^{v{8ytLe4*cJzOZBD$;vxUKVt3 zyQc(8!)sAw)=CpE;aO}@-(+xCfto%0M5ojR<0!`L6PGcmW`l(vqZuz|ugXQq%WtAO ze>g*~%m5%p!>Q0X#*S(bRk*Q{cSkcKD-8XbE z6Fh8e$1S!0{>wM9HtQsp0<+?qX7hJFOE&9Fb z@>N&0uadf(TDq;->g@DU-dqW7*RSesFSy^N{xznWxYV)u*5@OwTeF8~cMEUb$Ru&6 z!v9kFS<}X0kw|!D{jT|7iC)aO*q-;xfhWepx#lkqVYH*iFbOgg-@pT>)JIm;)W~z8$c|G```)Zc zMz+uT1KR)w$R}sSD-zwvKHjw|GB7CHs*@!FLL9rhRVt@`=#i<17K4?09aUzQ6;mwu zdzG(QJG=Aa?j6IrB=l_d7J(>0B5y$M6F1SmV_f89(>SmDWD<|WX^mncT>mqba{|b9+G@1YnSGYl$V7S+NF2xi~LM~bQE{)hU~+>Qpzy5I_d9k(`>@Sg(ss!^TdD+rEpdZ`~3hbMped_EP{Jv_FPxnYFnwocTW1fYW1}|J8 zaCQ0ZsBNdr9(AnRLGy7HZNxP%`W$&}y=V6%;N$V*x z%@Z6C5FJ+B#W}>u5wX;q{MexFTj)Jkbe-RqnKjWn{JK2R-Dm`A2NaEh5{E)^mOh`Ce^Q@L5W|4oH4fR{UdGBv}iy6Hr*(%%g+wM_#o4u~V7EzK_VBA~2pN*Ou zEP45WUgU#OOrCu2^a5>a6;UB!e754pU(5r}{z8aRdf~(M4tM@lP6);;k3;&Q9Sra` zgEl6$ohBe3ZC1rBSJUj7jk^3PCE9MC3yYI^9wO+)$Ql00$#c81+ai!_qOv%LS(OeGHOe`&QS!W(%t=Pwiil-^)JV0$ZSOP5 zVDa6mTqcno>-D`V>!(m5$2;w~6f%xJqXdL3cS*XX2$V5Ojm&bRtGeB?r@caT$R0r4 zFv+N7b=uH2Ki5ZgwBzB(kRe%OWffiJMt{+Zx8QtW_~|XwEgpVKslTXXQ=TK`MS11r z-1uWgHLLSfu+l+xn4UAyd8i2zxczNGao;{^T$|!`7Iqku*6loZ{S8XXV@gQE^5dCV zV=X4Mc%=lk@`q93dQ`M@=rQbio2}4-R2q#vCe=Qco3PU>%l@IwJ0bg9!W%OSwrSzA zqbj`(IH3pf<83|%#iDgJrh>VYEK$tiPbO;@zN)ia8lA5OeWza-<;=%(aw0W zw`ikFuElcFEk*f9ce~S0idxlYtVKQqYn#qh)$LmCgFV{Zw$l%K2cxOE)pi1K{TYec z)*ZDA+^K6eC$$HP+&!aizI*5Ri970xh{NK5!HvNN6R~&)rrh%cPEmf*cKd5I)gnGb-23q2GiT-i+$$?3HA)-fk-7 z;=^&Kly#Qmxqqq*2~U0L>52DHHM06pq^RVANuA0DH%PY4F|Axy=!IprfLs(0dbY4I z4qG@I6otm7GTr9t+kt%hkoj9!zRSAO)J8zS$k`F^-Mf@cp|z28*L`PWenwcfH>s%d zsj!f4XM!@y0)v4jZb+ZE}U@3+#5^#^~Jhas>tzGCOueu)< z-K4+0om1>;78daELX-9SSF3LJZGFwgFKO{< zbIC;L*ld-7Q}1Y%`@pwK@f_FhkNARD1`52Z%+Dtsb*__LGq7{l=A)l!6Iz&gm(OYjmgn#zsuE$lT^L?N68B>#r2$4cm@)D7v3eOl!?^84vbLnuW+O?V5e$ z&?Bk7iJ7FL_cXk@i%gf?W$yJB=Na3cZ;*8H!oAP7-S{9bHf+z|^bk2__()@kP*{o< zWeZe#Ac6&CrPOomQZ2(24&cD+zq%eE0T%Z($AX3rDQX907AQth$@gya>yxKCe(}wy zCnmZk&yA&$^ib8ba(3CsvA3R2Nf(yuSrw5VgxGp4y1lbOOXA6TbW_*iui573SKd$# z-#v}S;#^ZFwHZ>mXo_ZGgAJ{xuuaI*2!uUgkA&lz21hjl%_yC8ddsqyKHvM&oy9-9 zsPNKdRtiVo%(!fY_1H0OyEzNU3>I10a|XOdD^TTgeGaxmFF~D?FZG5mId|^>BWYN` zYantqOl&b9{0bLb#s{?Cy)AhKVf+Z>` zeBg&F3ZdpMw-1gtr8;4+c~}9m}>7oFJlEYq;h5a8C1zTehqT} z?T4nW4>POlOyDCHjq3;f!YR5}6n+ba|E(Pc^#p?B+B1(^Wu&3YL&Sf(jX!Uw_Siu` z%pxHn0f#h(qE>k-;@_|H+a3ZGSwa@J>yzrJ^kTTKbvs!aa6mWj*Hhe_i$at9V|u^0{^=%HrXwA{}dhEG64PDvS3GT#W~ zTqyTo^RLtQkB99ejUfsorMvz}=}nAkYfFpMkHGqIhiZO{WG#5aIxrIShkiD^@PBuLP6}0!`T7Yltkn9_J>kryaSgiz0y}j38|JgW+gmLH4_x|7@G5VRK8qx zvAdpVAk{=JLDTeVGVQH%*r&A8QK2PS-lvk^reD(5EI#mPej|zw9Vf479=Y@M%&H`B zo8#BT*QcW-WmURwxYr0iv!z(_YFzpA&g&??Z`O>aonpPB)$)n?+O#_+BGITFj!Umq zttB|_Pf6Wz7ty`<<#coqh35%Hu8mhBruXjp7<6qZ%Q1D5T~oObCbv00e29(ik|SBu z?Z$4cjT6)!3;lu?8Y=c0_8@{aPGQU0th#nk;U zt}Lt6rtyu}B9p;I54+IuTT4WL%pBhWwq;=4^hL0>+JZ%5wV|l0=~lf;2I{L`?Gm%x z>E(HU({|lv@;`6qZ=a-Z5)5z*x6i~9vb<_Lq{tHP00WPY>|*)(R7y{Qsd?h!30VbZ z8fbXsvFPESkDnhOEcA7{>FpL+jY_eJxA{)~ykROVpHc=4DifMXvq!i7h7JGwg!e6l zW)l+=*9f~5VQ2E14taFyFZ<=66VAsQX69ahmmq;MHOzG`DR#wwzt*q)@TDS$Uq8Bc z?Kp~6AFkM$x*qiBRsQ;Ejw6|wl?tuLO0i=0T~ALhZ&X9{|zdp%7C!Ql9|8E)_ z>rm+rp{v{PsZY!Qd7pp%v=76+`}&y$VFVXE8{FNCVL!hh=czvjN$x*9NPd|8n{#x& z=`b}|qmm*||9sR#Q2Bbd|6dy<7{gPPb!Rb3g)r=?@^fT=n~r}>_^!*+GD;j&1xlEGp1fIu$v|@~U|MIotpGDLf1o+MKA(eLCOcFt@KuPhft2MN}m`qOpu8 zBs@9r7iO;0Wp+Pr7!2?9@w?K@$)3)OZMt2Qk9tlflv*tj+2;MekJ{7jg`E#ZoCpG$(H@uk(kqzYShFkj6R;^uDMphpKaY*i=tUL==Jz5+*MJJO#Iu5|5vW_-l~UmxVO z>TzpuNBgt4(swv9{*Rx}8W;+601w5z?*x(mpN{s| z6fpHhKGM?2Y52k`Lr`-6>=C?SgOdp@E%E7aNn|Ms>fVP<<==LZU;nvFdF|nUb1TQ- z)8EQ*(QDWL&ILG2N=Sjk4VIU;{~>^Jp@Q>Av!3aD)SLg)-Id2fy?=dcSCpt3Zj>z* zGNdd;!jM9;lPzm@qhyH**~${R6+$J-He?+JV_(uu_GB4Li+wMQvgA4Qdw#cjpZkpG zcmKGr*L}U7|K{~FKJ)#a&-t9sIp_U8=kUP;(eJ)Z?O`D@eouKFg+?<=V=#Qe*~i;F zx42QjrZTP2)6v4$wof#;8&iFIKDKyJR5P=EcNAB_bAR4uCZS99?b#NzZt^uKnEI;n zWr3X3M#o2}FBl^Le-=K*zJorHa{CLKJorvcnvf;kξKLGwqd2vqG*_-9W+4bR{0 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 Fastify API + | |-- React SPA / REST / SSE + | |-- SQLite / LocalDataRoot + | |-- Resend / Amap HTTPS + |-- runtime/node.exe -> Generation Worker + | |-- SQLite durable queue + | |-- AI Gateway HTTPS + | |-- cleanup / retention / measurement + |-- Chrome or Edge -> http://127.0.0.1:43121 + +Read-only asset roots --(resource ID + manifest)--> Fastify API +Asset compiler --(declarative metadata / derived files)--> Git metadata or LocalDataRoot +``` + +- **Supervisor**:只管理实例、配置、子进程、托盘状态和浏览器,不读取业务表,不持有用户会话。 +- **Web**:只保存当前页面内存中的未提交/未保存编辑态;不直接访问文件系统或外部供应商。 +- **API**:唯一业务写入口;执行鉴权、配额、状态版本、点数与任务创建事务;提供资源 ID 访问。 +- **Worker**:唯一 AI 执行入口;不对浏览器开放端口;通过 SQLite 领取任务并完成结算或释放。 +- **SQLite**:业务真相源和持久队列;不存二进制内容和真实密钥。 +- **LocalDataRoot**:存数据库、日志和 Dada 管理的私有/派生文件;不包含规范素材全库。 +- **素材编译器**:TypeScript CLI,只读解析规范归档,输出声明式目录、校验报告和必要派生文件;不执行 Lua/Prefab。 +- **发布组件**:Gitea Actions 生成前端、后端 bundle、托盘程序、随包 Node 运行时、迁移、manifest 和 SHA-256。 +- **浏览器支持门禁**:由 Fastify 的最小 gate shell、支持检查接口和产品 API 前置中间件共同执行;不依赖登录态,不把浏览器兼容判断下放给 React 产品路由。 + +### 2.3 不采用的基础设施 + +P0-A 不采用 Docker、Redis、远程任务队列、远程数据库、对象存储、公司服务器、外部日志平台、外部指标平台、自动更新服务或自动扩容。GenerationJob 表和 SQLite 租约足以支持当前单机且每账号单任务的容量。 + +## 3. 运行、环境与发布 + +### 3.1 便携包布局 + +```text +Dada-P0A--win-x64/ + Dada.exe + runtime/node.exe + server/api.mjs + server/worker.mjs + server/native/better_sqlite3.node + web/assets/... + migrations/... + asset-metadata/... + LICENSES/... + RELEASE.json + START-HERE.txt +Dada-P0A--win-x64.zip.sha256 +``` + +ZIP 不包含真实密钥、管理员邮箱、LocalDataRoot、只读素材二进制全库或验收账号。`RELEASE.json` 固定记录 app/schema 版本、构建提交、Windows 验收 build,以及 Chrome/Edge 各自的受支持产品标识、稳定版 major、实际验收 full version 和记录时间;发布门禁只接受记录的 major,patch 更新仍属于同一受支持 major。`START-HERE.txt` 固定说明 SHA-256 核验、未签名状态、SmartScreen 首次运行提示和杀软误报时核对下载制品/Gitea 构建记录的流程;不指导关闭杀软、添加宽泛排除或跳过哈希验证。更新时先从托盘退出,再整体替换程序目录;数据库迁移在下一次启动时事务化执行。P0-A 不支持数据库降级,也不为迁移创建自动备份副本;迁移失败时停止启动并保持原事务未提交状态。 + +### 3.2 Supervisor 生命周期 + +1. 获取 Windows 命名互斥量 `Dada.P0A.Instance`。第二次启动通过命名管道通知现有实例打开网页,然后退出。 +2. 读取 `%LOCALAPPDATA%\Dada\P0A\config\instance.json` 中的非敏感配置引用,检查 LocalDataRoot 不在程序、Git、项目或下载目录内。已初始化实例的根目录或数据库缺失时停止并报告 data_missing,不自动重建或从残留文件恢复;部署人员只能显式初始化一个新的空实例。 +3. 检查 Windows 凭据管理器中必要条目的存在状态。启动子进程时,Supervisor 读取所需条目并通过仅对应子进程继承的匿名管道一次性注入内存;值不进入命令行、环境变量、文件或日志。API 只取得 Resend/高德凭据,Worker 只取得 AI 网关凭据。 +4. 检查只读素材根目录、manifest 哈希、LocalDataRoot 可写性和 `127.0.0.1:43121` 端口;端口占用时停止,不动态换端口。 +5. 启动 API。API 打开 SQLite、执行事务迁移、完成容量对账并暴露仅回环可访问的健康端点。 +6. API ready 后启动 Worker;Worker 注册租约拥有者并执行过期租约恢复。 +7. 两个子进程均健康后,Supervisor 读取 `RELEASE.json` 并探测已安装浏览器版本:优先启动受支持 Chrome,其次为受支持 Edge;只有不支持版本时仍可打开 localhost 的阻断引导页,不进入产品。托盘菜单固定提供打开 Dada、用 Chrome 打开、用 Edge 打开、运行诊断、重试受影响组件和退出;选择不支持或无法识别的浏览器只得到阻断引导。 +8. 子进程异常退出时按 1 秒、5 秒、15 秒最多自动重启三次;五分钟内仍失败则进入 degraded,不继续循环重启。API degraded 时网页不可用;Worker degraded 时 API 禁止新任务但保留查看、下载、编辑和删除能力。 +9. 托盘退出通过受 Supervisor 控制的命名管道要求 Worker 停止领取任务并让 API 停止接收写请求;等待最多 15 秒后终止残留子进程。运行中的任务按第 8.4 节恢复。 + +### 3.3 离线配置命令 + +同一个 `Dada.exe` 提供以下部署人员命令;这些命令不是产品后台能力: + +- `Dada.exe configure init`:创建非敏感实例配置和默认 LocalDataRoot; +- `Dada.exe configure data-root`:校验并设置 LocalDataRoot; +- `Dada.exe configure asset-root`:按资源类别设置只读根目录引用; +- `Dada.exe secrets set|status|clear`:通过隐藏输入维护 Windows 凭据管理器条目;`status` 只返回已配置/未配置; +- `Dada.exe admin-allowlist add|remove|status`:维护管理员白名单摘要;`status` 只显示数量与配置版本; +- `Dada.exe doctor`:检查端口、目录、manifest、SQLite、浏览器和外部依赖配置状态,不输出密钥或白名单值。 + +所有修改配置的离线命令必须取得同一个实例互斥量;Dada 正在运行时拒绝修改,要求先从托盘退出。命令先完成语法、路径和必需凭据引用验证,再使用同目录临时文件和原子改名写入非敏感配置;每次成功写入将 `secure_config_revision` 递增 1。离线命令不连接产品数据库,也不直接写产品审计。`configure data-root` 不移动、复制或导入旧数据,只能在首次初始化时设置,或明确建立一个空的新实例;原目录仍由部署人员自行保留或处理,Dada 不提供恢复入口。 + +管理员白名单使用凭据管理器中的 pepper 生成规范化邮箱 HMAC,配置文件只保存 HMAC 集合。白名单邮箱完成验证码验证后,产品数据库才保存其管理员账号邮箱。API 启动时将配置的 `secure_config_revision` 与数据库 `secure_config_apply_state.applied_revision` 比较: + +1. 修订未变时不重复应用或记录审计。 +2. 新修订通过完整性、pepper 可用性、HMAC 格式和身份冲突检查后,在单一 `BEGIN IMMEDIATE` 事务中应用管理员访问变化、撤销被移除或失效管理员的全部会话、写入 `actor_type=system`、`actor_ref=backend_secure_config`、`result=succeeded` 的非敏感 AdminOperationLog,并推进 applied revision。事务提交后新配置才生效。 +3. 验证或应用失败时回滚所有业务变化且不推进 revision;数据库仍可写时,另起一个事务记录不含邮箱、HMAC、pepper 或配置值的 `result=failed` 审计。API 不进入 ready,由托盘报告安全配置待修正。 +4. 成功或失败审计无法按上述规则持久化时,配置不生效且 API 启动失败。离线命令执行者身份永不进入产品审计。 + +### 3.4 环境关系 + +| 环境 | 数据根 | 外部服务 | 浏览器 | 用途 | +| --- | --- | --- | --- | --- | +| 本地开发 | `%LOCALAPPDATA%\Dada\P0A-dev\` | 默认模拟;显式契约验证命令才允许真实调用 | 开发服务器加本机 Chrome/Edge | 日常开发;允许开发初始化脚本预置普通测试账号 | +| CI | `%RUNNER_TEMP%\dada-ci\` | 全部模拟;只有脱敏契约 fixture | Gitea Runner 上的 Chrome/Edge | 单元、集成、浏览器、视觉和打包;禁止读取真实 LocalDataRoot、凭据和账号 | +| P0-A 验收 | `%LOCALAPPDATA%\Dada\P0A\data` 或离线配置的合规目录 | 真实 AI 网关、Resend、高德 | 当前稳定版 Chrome 和 Edge | 由项目方控制的 1 个 super_admin 和 1 个普通账号人工验收 | +| P0-A 发布运行 | 与验收实例相同 | 与验收实例相同 | 与验收实例相同 | P0-A 没有独立远程生产环境 | + +CI 的真实素材发布检查可只读访问规范素材根目录,但使用独立输出目录,不能读取真实 LocalDataRoot,也不能复制全量素材。日常 CI 使用小型合成 fixture,不扫描 3.57 GB 归档。 + +CI 的单元/API/Worker 测试使用系统分配的临时回环端口;只有 P0-A 打包边界测试使用 43121。Gitea 对浏览器和打包 job 设置并发数 1,运行前确认真实 Dada 实例未启动,绝不停止或复用真实实例。Chrome/Edge 均使用 `%RUNNER_TEMP%` 下的一次性 user-data-dir,测试结束删除,不读取项目方日常浏览器 profile。 + +### 3.5 配置分层 + +- **编译配置**:应用版本、Schema 版本、固定端口、5 GB/150 MB 固定上限、固定错误注册表和发布浏览器支持记录,进入 Git/发布制品且后台不可修改。 +- **非敏感运行配置**:`secure_config_revision`、LocalDataRoot 引用、素材根引用、浏览器启动偏好(不含支持品牌/版本)和日志级别,保存在 ACL 限制为当前 Windows 用户的 `instance.json`;路径不返回前端。 +- **安全配置**:AI 网关、Resend、高德密钥和管理员白名单 pepper,保存在 Windows 凭据管理器。 +- **业务配置**:ModelConfig、邀请码、点数、模板状态、用量和服务状态,保存在 SQLite 并审计。 + +### 3.6 浏览器支持硬门禁 + +Fastify 为所有访问先提供独立、最小的 support gate,不直接提供 React 产品 shell。门禁流程固定如下: + +1. `GET /`、直接访问 `/app/*` 或 `/admin/*` 时只返回 gate HTML、其独立 CSS/JS 和 `RELEASE.json` 中的非敏感支持摘要;响应声明 `Accept-CH: Sec-CH-UA, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform`。阻断页不加载产品 bundle、公开素材 manifest、登录组件或 Service Worker。 +2. gate 使用 `navigator.userAgentData.getHighEntropyValues(["fullVersionList", "platform"])` 取得 Chromium User-Agent Client Hints,并与请求头中的 `Sec-CH-UA`、`Sec-CH-UA-Full-Version-List`、`Sec-CH-UA-Platform` 交给 `POST /api/v1/support/check` 交叉校验。只接受 platform 为 Windows,且品牌明确为 `Google Chrome` 或 `Microsoft Edge`,major 与 `RELEASE.json` 对应记录相同;不使用 UA 字符串猜测 Chromium 品牌,UA-CH 缺失、冲突、解析失败或无法排除其他浏览器时统一按不支持处理。 +3. 通过后 API 设置签名 HttpOnly、SameSite=Strict、Path=/ 的 `dada_browser_support` Cookie,内容只含 app_version、品牌、major 和签发时间,不含指纹或用户身份。Cookie 最长 24 小时、API 重启后失效;产品 API 中间件每次仍比对当前 `Sec-CH-UA` 的品牌/major,浏览器升级、标识缺失或 app_version 变化时必须重新检查。 +4. 除 support gate 静态文件、`POST /api/v1/support/check` 和 Supervisor 健康探针外,全部产品 HTML、静态 bundle 和 `/api/v1` 路由都要求门禁通过。失败统一返回 HTTP `426`、`code=BROWSER_UNSUPPORTED`、`message_key=browser.unsupported`,`details.reason` 只允许 `platform_unsupported`、`brand_unsupported`、`version_unsupported` 或 `identity_unavailable`,并返回受支持品牌/major;不得返回继续令牌或绕过参数。 +5. 发布时 WP-7 先从 Chrome/Edge 官方稳定通道安装结果和浏览器可执行文件版本生成候选记录,再在该精确 full version 完成 AC-24/视觉/性能验收后写入 `RELEASE.json`。运行时不联网查询“当前版本”,不接受验收时前一 major,也不允许后台修改支持记录。 + +### 3.7 辅助系统界面契约 + +辅助系统界面只呈现本机进程与诊断,不新增产品角色或部署人员 CLI。稳定状态及动作如下: + +| 状态 | 产生者与判定 | 用户允许动作 | 产品功能影响 | +| --- | --- | --- | --- | +| `starting` / `ready` | Supervisor 汇总预检、API ready 和 Worker heartbeat | 打开 Dada、选择受支持浏览器、运行诊断、退出 | starting 时产品 API 不开放;ready 时按业务状态开放 | +| `startup_failed` | Supervisor 的配置、凭据存在性、目录、迁移或子进程启动检查失败 | 查看诊断结果、修正外部条件后重试启动、退出 | 全部产品功能阻断 | +| `port_in_use` | Supervisor 无法独占绑定 `127.0.0.1:43121` | 运行诊断、释放端口后重试、退出 | API/Worker 均不启动;不动态换端口 | +| `api_degraded` | API 连续健康失败且有限自动重启耗尽 | 重试 API、运行诊断、退出 | 网页和全部产品 API 阻断;Worker 停止领取新任务 | +| `worker_degraded` | Worker heartbeat 超时或有限自动重启耗尽 | 打开 Dada、重试 Worker、运行诊断、退出 | 禁止新 GenerationJob;查看、编辑、纯 JSON 保存、下载和允许的删除继续 | +| `diagnostics_running` / `diagnostics_ready` | Supervisor 调用本地只读探针并汇总 API/Worker 非敏感结果 | 重新运行、复制脱敏摘要、返回托盘 | 诊断本身不恢复、不停用业务能力,也不修改配置 | + +托盘和诊断结果只可读取 `app_version`、`supervisor_state`、`component`、稳定 `check_code`、`result=pass|warning|fail`、`message_key`、`checked_at`、固定端口与 `bind_scope=loopback`、浏览器支持版本记录、目录 `configured/readable/writable` 布尔值、manifest 哈希是否有效、SQLite/migration 状态、API/Worker 状态与 heartbeat age、LocalBackendStorageState 数值摘要,以及外部凭据是否已配置和服务状态。不得返回密钥、白名单值、邮箱、提示词、私有正文、图片、绝对路径、供应商原始错误或崩溃堆栈。UIDesign.md 负责托盘、启动失败、端口占用、degraded 和诊断结果的视觉呈现,但不得增加绕过、动态换端口、自动付费、打开私有目录或新的 CLI 操作。 + +## 4. LocalDataRoot 与文件访问 + +### 4.1 固定目录布局 + +默认根目录为 `%LOCALAPPDATA%\Dada\P0A\data`: + +```text +data/ + db/dada.sqlite3 + db/dada.sqlite3-wal + db/dada.sqlite3-shm + content/references// + content/generated// + content/exports///latest.jpg + content/exports///latest.png + managed-assets//original/ + managed-assets//thumbnail/ + derived-assets// + staging//... + logs/api/*.jsonl + logs/worker/*.jsonl + logs/supervisor/*.jsonl +``` + +数据库、WAL、审计记录和日志位于 LocalDataRoot,但不计入 `managed_content_bytes`。`content`、`managed-assets` 和 `derived-assets` 中由 Dada 管理的二进制文件计入,其中 `managed-assets` 明确包含后台上传普通贴纸原图和 Dada 生成的缩略图。既有只读素材根、程序文件、浏览器公开缓存和用户下载目录不计入。 + +目录的写入边界固定如下: + +- 约 3.57 GB 规范素材只存在已配置的只读源根目录,不进入 Git、`managed-assets`、`derived-assets` 或 LocalDataRoot 其他目录。 +- Git 只保存版本化 manifest、catalog、metadata、Schema、版本号、哈希和文本校验报告,不保存规范素材二进制或其等价副本。 +- `managed-assets` 只保存 super_admin 在产品上线后上传的 PNG/WebP 普通贴纸原图及 Dada 生成的必要缩略图;两类文件分别登记 `file_kind=sticker_original|sticker_thumbnail`、实际字节和哈希,均计入 5 GB,不接收现有归档素材的复制。 +- `derived-assets` 只保存浏览器和本地后端无法从规范源直接安全服务的最小运行产物;以内容 SHA-256 寻址并跨版本复用,禁止预转换全库、批量等价复制或为便利建立第二套素材库。 + +### 4.2 文件标识与路径隔离 + +- 所有私有文件使用随机 UUIDv4 `asset_id`;数据库只保存 `storage_class`、相对对象键、大小、MIME、SHA-256、所有者和状态。 +- API 响应只包含资源 ID 与受控 API URL,不包含绝对路径、素材根引用或可推导的对象键。 +- API 使用 `Path.GetFullPath` 等价的规范化检查确保最终路径仍在已配置根目录内,拒绝 `..`、符号链接逃逸和不在 manifest 中的资源。 +- 浏览器不得使用 `file://`、绝对路径或 File System Access API 读取 LocalDataRoot 或素材根;用户从标准文件选择器选择待提交参考图不受影响。 +- `public_release_asset` 使用公开版本化路由;`internal_preview_asset` 和 `private_user_asset` 使用不同路由、缓存头和鉴权中间件,不能共享静态目录。 + +### 4.3 文件与数据库一致性 + +所有二进制写入采用“预检 -> staging -> 内容校验 -> 原子改名 -> SQLite 提交 -> 异步补偿”的固定流程: + +1. 根据请求声明大小、当前实际字节数和存储预留计算预计写入。准入比较固定为 `managed_content_bytes + active_storage_reservations + projected_write_bytes > hard_limit_bytes` 时拒绝;预计写入后恰好等于硬上限时允许建立预留并继续,不能使用 `>=` 作写前拒绝。 +2. 文件流写入同一卷的 `staging/`,同时计算 SHA-256、实际大小和 MIME;禁止把整个文件读入内存。 +3. 校验成功后原子改名到内容寻址目标;SQLite 事务登记 `managed_file`、资源关系和实际字节增量。提交后总量恰好等于硬上限时,本次写入成功并在同一事务把 `storage_status` 置为 full。 +4. SQLite 事务失败时,将已改名但未引用的文件加入补偿清理;进程崩溃后由启动对账识别孤儿。 +5. 数据库先进入 `purged` 或删除身份关系后,文件立即无法通过 API 读取;物理删除由清理队列完成,完成后再减少 `managed_content_bytes`。 + +成功生成输出写入前使用 ModelConfig 中经契约验证的最大响应字节建立 `storage_reservation`。未验证模型必须保持 `available_for_new_jobs=false`,但不因此改写 configured enabled/default/priority。导出持久化失败或容量 full 时,浏览器仍可下载同一次客户端合成结果,但不更新 `latest_exports`。 + +`PUT /projects/{id}/state` 是纯 JSON 事务,不调用上述二进制流程。服务端对其中每个资源引用校验已提交、未撤销、属于当前用户且对当前项目可访问;该路由不得隐式上传、复制、替换或派生任何文件。需要新二进制的参考图、素材、派生物和 latest export 必须使用独立路由和容量预检。 + +### 4.4 LocalBackendStorageState 实现 + +- 固定 `hard_limit_bytes = 5,368,709,120`。 +- `normal`:小于 `4,294,967,296`;`warning`:达到该值且小于 `4,831,838,208`;`critical`:达到该值且小于硬上限。 +- API 每次受管文件提交或物理删除均在同一数据库事务更新计数;启动时在开放写能力前做全量文件对账,运行中每 60 秒执行可写性和磁盘余量探测。 +- 已提交 `managed_content_bytes >= hard_limit_bytes` 或已承诺的 `managed_content_bytes + active_storage_reservations >= hard_limit_bytes` 时,持久状态为 full 并拒绝新的受管二进制写入。单次写入准入仅在预计总量 `> hard_limit_bytes` 时拒绝并返回容量错误;预计总量 `= hard_limit_bytes` 时允许本次提交,提交后进入 full。因单个过大请求被拒绝但实际量与现有预留仍低于上限时,不伪造持久 full,较小且可容纳的后续请求仍可重新预检。 +- 数据库目录、日志目录、LocalDataRoot 不可写,或系统磁盘无可用空间时设为 `unavailable`。API 和 Worker 在该状态停止新增写入与 AI 调用。 +- warning/critical 只产生非阻断状态事件,不删除文件。critical 的引导顺序固定为下载成品、永久删除项目或由 super_admin 显式清理符合条件的无引用历史贴纸文件、等待物理清理与重测、恢复后继续生成。 +- full 仍允许已有项目编辑和上述纯 JSON 项目状态保存、查看、下载、客户端仅下载导出、永久删除,以及显式提交符合条件的历史贴纸清理;它阻止新 AI 任务、参考图持久化、后台贴纸原图上传、缩略图生成、其他受管/派生二进制新增和 latest export 保存。unavailable 拒绝包括项目 JSON、贴纸上传和缩略图在内的所有后端新增写入,但保留查看、下载、客户端仅下载导出和允许的删除入口;未保存编辑可以留在页面内存但不能显示为已保存。若 SQLite 仍可写,永久删除或显式贴纸清理可先提交逻辑删除/队列;SQLite 自身不可写时明确报告操作未提交并允许恢复后重试。 +- 物理删除完成、全量重测低于硬上限且目录可写后,状态才恢复 active。 + +自动清理只包括 PRD 已要求的 720 小时项目清理、账号注销、180 天保留期和 24 小时以上无引用 staging;不得因容量告警删除仍由用户拥有的内容,也不得自动清理任何后台上传普通贴纸历史原图或缩略图。贴纸历史文件在物理删除完成前始终计入 `managed_content_bytes`。 + +### 4.5 日志与数据库维护 + +- API、Worker、Supervisor 使用 JSONL 结构化日志,每文件 10 MiB,每进程最多保留 10 个轮转文件,并删除超过 30 天的诊断日志。 +- 日志只包含 correlation ID、内部非敏感对象 ID、状态类别、耗时和错误类别;禁止提示词、邮箱正文、验证码、密钥、会话令牌、图片、绝对路径和供应商原始错误正文。 +- Worker 每日执行 180 天保留期清理;每周在无运行任务时执行 WAL checkpoint 和 incremental vacuum。 +- CreditLedger、AdminOperationLog 与 PrivateContentAccessLog 通过 SQLite trigger 禁止普通 UPDATE/DELETE。只有 Worker 的保留期清理连接可在注册的 `dada_allow_retention_purge()` 作用域内删除到期审计;账号注销只能在 `dada_allow_privacy_purge()` 作用域中把原点数事件转成 AnonymousRetainedEvent 后删除身份关联流水。 +- 迁移、checkpoint、日志写入或审计写入失败均使 `storage_status` 进入 unavailable,并阻止后续 AI 调用。 + +## 5. 工程数据模型 + +### 5.1 SQLite 约束 + +数据库启动固定执行:`journal_mode=WAL`、`foreign_keys=ON`、`synchronous=FULL`、`busy_timeout=5000`。涉及任务、点数、名额、邀请码、配置版本和审计的写事务使用 `BEGIN IMMEDIATE`,避免 API 与 Worker 各自读后写造成竞态。 + +所有时间以 UTC 毫秒保存,对外使用 ISO 8601;用户界面再按本机时区显示。私有对象使用 UUIDv4。点数使用整数。枚举由数据库 CHECK、TypeBox Schema 和前端生成类型三层一致约束。 + +### 5.2 十九个产品数据契约映射 + +| 产品契约 | 工程映射 | 关键约束 | +| --- | --- | --- | +| 17.1 UserProfile | `users`、`user_profiles`、`credit_accounts`、`privacy_consents` | 规范化邮箱唯一;role 仅 user/super_admin;名额由 role、status、counts flag 计算;super_admin 的私有内容告知版本/时间由后端持久化;删除后移除身份字段 | +| 17.2 ModelConfig | `model_config_sets`、`model_config_versions`、`model_config_current`、`model_runtime_availability`、`gateway_route_profiles`、`error_mapping_profiles` | 配置集合不可变并原子切换;每集合恰好一个 enabled default;推荐优先级为集合内唯一正整数;运行时可用性独立且不回写配置;契约字段变化强制 unverified | +| 17.3 Project | `projects`、`project_states`、`latest_exports` | `state_version` 单调递增;比例/尺寸创建后不变;active 上限在事务内检查;purge_at 精确为 720 小时 | +| 17.4 GenerationJob | `generation_jobs`、`generation_job_references`、`job_leases` | 每账号 queued/running 部分唯一索引;配置、成本和参考图关系均快照;终态不可逆;上游不确定性只作内部诊断 | +| 17.5 CanvasElement | `project_states.canvas_json` 中经版本化 JSON Schema 校验的元素数组 | 最多 50;不持久化 Fabric 私有字段;动态值、色卡五色和 DYN004 坐标按产品字段快照 | +| 17.6 TemplateRegistry | `template_registry`、`template_versions`、`test_batches`、`template_batch_members` | 稳定 ID + resource_version 唯一;状态转换受服务校验;full_p0 不能逐批公开 | +| 17.7 StaticStickerCatalog | `static_sticker_catalog`、`static_sticker_versions`、`project_asset_refs`、`managed_files` | part1-25 和顺序稳定;记录 bundled/admin 来源及原图/缩略图文件关系;重复图片保留不同 ID;已发布版本与 active/trashed 项目引用阻止历史文件清理 | +| 17.8 DynamicStickerData | 固定 `dynamic_provider_registry` 代码注册表和 CanvasElement 实例快照 | 只提供模板声明的字段;DYN004 坐标仅在同意后写入;不执行 Lua | +| 17.9 CreditAccount 与 CreditLedger | `credit_accounts`、append-only `credit_ledger` | 余额 CHECK 和事务锁;reserve/commit/release 操作键唯一;管理员调点原因必填 | +| 17.10 PrivateContentAccessLog | append-only `private_content_access_logs` | 打开前先写成功审计;180 天;注销时目标改为不可反推随机标识 | +| 17.11 AnonymousRetainedEvent | `anonymous_retained_events` | 注销事务生成随机主体且不保存映射;无邮箱、IP、正文、资源或原 ID;最迟注销后 180 天删除 | +| 17.12 AssetReleaseManifest | Git 中的 immutable manifest + `asset_releases`、`asset_release_items` | release_version、ID、相对路径、大小、哈希和 access_class 固定;公开导出只含 public | +| 17.13 AssetPreviewGrant | `asset_preview_grants` | active/revoked/expired;每次 manifest 和资源读取重新校验;不改变用户角色 | +| 17.14 ClientCachePolicy | 前端固定配置 + 只含公开资源元数据的 IndexedDB `public_asset_lru` + Cache Storage | 157,286,400 字节 LRU;不存内部预览、私有内容、项目、验证码或会话 | +| 17.15 ExternalServiceUsage | `external_service_usage` | Resend 日/月和高德月度独立行;配额领取事务化;只含四个固定状态;恢复审计 | +| 17.16 AdminOperationLog | append-only `admin_operation_logs` | actor、结果、非敏感前后摘要;180 天;配置生效使用 system/backend_secure_config;贴纸清理请求、引用复核、拒绝/排队和物理结果均留痕 | +| 17.17 GatewayBalanceState | `gateway_balance_states`、`gateway_balance_affected_models` | model/account/unknown 影响范围;真实确认后才能恢复;不保存密钥或原始供应商正文 | +| 17.18 GenerationErrorCategory | 代码和迁移共同种入的九项固定只读注册表 `generation_error_categories` | 后台不可增删改;mapping profile 只能引用固定 ID;稳定 message_key | +| 17.19 LocalBackendStorageState | 单实例 `local_backend_storage_state`、`managed_files`、`storage_reservations` | 名称固定;原图/缩略图计量;预计量大于上限才拒绝、等于上限允许后进入 full;物理删除后重测;不与浏览器 localStorage 混淆 | + +### 5.3 支撑表 + +以下表只支撑冻结产品语义,不形成新产品对象: + +- `invite_codes`、`email_challenges`、`sessions`、`auth_rate_limits`:邀请码、10 分钟一次性验证码、60 秒重发限制、30 天会话;验证码只保存带 pepper 的摘要。 +- `idempotency_records`:路由、主体、键、请求哈希和响应引用。 +- `managed_files`、`file_cleanup_queue`、`storage_reservations`:文件一致性、清理和容量预留;贴纸原图/缩略图分别记录 file_kind,排队删除不提前扣减容量。 +- `project_asset_refs`、`asset_cleanup_requests`、`asset_cleanup_request_items`:从每次已提交项目状态提取资源版本引用,并保存管理员显式贴纸清理的候选快照、确认、复核与物理结果;只属工程支撑,不新增产品对象。 +- `model_config_sets`、`model_runtime_availability`:前者把三个模型的不可变配置版本作为一个可原子激活的集合,后者只记录逐模型 `available_for_new_jobs`、非敏感 reason、检测与恢复时间。 +- `browser_support_release`:从 `RELEASE.json` 导入当前 app_version 的 Windows、Chrome/Edge 品牌、major 和已验收 full version,只供门禁与诊断读取,后台不可编辑。 +- `job_leases`、`worker_heartbeats`:Worker 领取和崩溃恢复。 +- `outbox_events`:数据库提交后向 SSE 和清理队列发布,不承担跨机器消息。 +- `recent_assets`:账号级文字模板和贴纸最近使用,不存未保存项目状态。 +- `service_recovery_checks`:辅助服务和网关恢复的非敏感确认结果。 +- `secure_config_apply_state`:已生效安全配置修订号、非敏感摘要和应用时间;不保存白名单、HMAC、pepper 或凭据。 +- `generation_jobs.upstream_outcome_known` 与 `upstream_cost_reconciliation`:只供后台诊断与本地监控的上游结果/成本核对标记,不是新的 GenerationJob 状态、点数状态或 GenerationErrorCategory。 + +v1.10 同步迁移固定完成四项变化:为 `user_profiles` 增加可空的 `private_content_notice_version` 与 `private_content_notice_acknowledged_at`;建立不可变 `model_config_sets`、集合约束和独立运行时可用表;为后台贴纸原图/缩略图补齐 `managed_files` 关系、项目资源引用和显式清理表;导入当前发布浏览器支持记录。迁移只向前执行,先回填/校验再切换 Schema 版本,任一校验失败整体回滚并阻止 API ready。 + +## 6. API 与事件契约 + +### 6.1 通用协议 + +- 生产 API 前缀固定为 `/api/v1`,JSON 使用 UTF-8;文件上传使用 multipart 流式处理。 +- 生产只接受 Host `127.0.0.1:43121` 和同源 Origin;不开放 CORS。开发环境只允许显式 Vite origin。 +- 浏览器支持门禁中间件位于路由解析后的第一层,先于会话、CSRF、角色和业务处理;除 support check 与 Supervisor 健康探针外,未通过门禁的请求不得触发数据库业务读取、验证码发送、资源读取或外部调用。 +- OpenAPI 3.1 从 TypeBox Schema 生成;前端客户端由同一 Schema 生成,不能手写漂移类型。 +- 每个响应包含 `X-Correlation-Id`;客户端可传入合法 UUID,否则 API 生成。 +- 创建型和有金融/文件副作用的请求要求 `Idempotency-Key`。键为客户端随机 128 位以上值,不包含用户数据。 +- 列表使用 opaque cursor;不得用连续数据库 ID 暴露数量或顺序。 + +统一错误信封: + +```json +{ + "error": { + "code": "STABLE_ENGINEERING_CODE", + "message_key": "stable.ui.message.key", + "correlation_id": "opaque-id", + "error_category": "optional-generation-category", + "details": {} + } +} +``` + +`details` 只允许字段级校验、最新版本、容量状态或当前任务引用等白名单信息。禁止供应商原始错误、堆栈、内部 URL、绝对路径和凭据。 + +### 6.2 API 分组 + +| 分组 | 主要路由 | 语义 | +| --- | --- | --- | +| 浏览器支持 | `POST /support/check`;产品前缀外的 Supervisor health | 读取 UA-CH 与发布支持记录;仅返回支持/阻断状态和非敏感版本;不建立产品会话 | +| Bootstrap | `GET /bootstrap` | 仅门禁通过后返回应用版本、公开功能状态、模型配置/运行时摘要和非敏感依赖状态 | +| 普通认证 | `/auth/register/*`、`/auth/login/*`、`/auth/session`、`/auth/logout` | 注册/登录分离;发码前校验状态;验证码成功后创建会话 | +| 管理员认证 | `/admin-auth/login/*`、`/admin-auth/session` | 发码前校验 HMAC 白名单;首次验证创建 super_admin;session 返回当前私有内容告知版本、已确认版本和 `notice_acknowledged` | +| 账号 | `/me/profile`、`/me/credits`、`/me/credit-ledger`、`/me/delete` | 资料、点数、注销;注销为不可逆事务 | +| 模型 | `GET /models`、`GET /models/{model_id}`、`PUT /admin/models/configuration` | 读取同时返回 `configured_default_model_id`、逐模型配置/运行时状态和 `recommended_model_id`;后台以完整配置集合原子保存 | +| 生成 | `POST /generations`、`GET /generations/{id}`、`GET /generations/current` | multipart 提交;原子冻结;已有活动任务返回现有任务而非创建第二个 | +| 项目 | `/projects`、`/projects/{id}`、`/projects/{id}/state`、`/projects/{id}/trash|restore|purge` | 所有者鉴权;自动保存使用 If-Match state_version;回收站与清理 | +| 历史与成品 | `/projects/{id}/images`、`/projects/{id}/exports` | 原图下载、历史删除、latest JPG/PNG;资源 ID,不暴露路径 | +| 素材 | `/assets/public/*`、`/assets/preview/*`、`/private-assets/*` | 三类资源使用独立授权和缓存策略 | +| 管理员贴纸清理 | `GET /admin/assets/history-cleanup-candidates`、`POST /admin/assets/history-cleanup-intents`、`POST /admin/assets/history-cleanup-intents/{id}/confirm` | 只返回无引用候选;确认时事务复核全部引用并整体拒绝或排队;物理结果异步回读 | +| 私有内容告知与访问 | `POST /admin/private-content-notice/ack`、`/admin/private-content/*` | ack 按当前 super_admin 和版本持久化;未确认禁止进入区域;每次正文/图片打开仍先写独立访问审计 | +| 管理后台 | `/admin/users`、`/admin/invites`、`/admin/models`、`/admin/assets`、`/admin/preview-grants`、`/admin/services`、`/admin/audit` | 全部要求 active super_admin;敏感修改写 AdminOperationLog | +| 状态事件 | `GET /events` | 同源鉴权 SSE;任务、保存、存储和服务状态;事件不携带私有正文 | + +SSE 只作为状态变化提示,事件体固定为 event_id、event_type、entity_ref、state/config version 和 occurred_at。客户端收到后通过 REST 读取真相;断线或 event_id 不连续时执行一次 bootstrap/refetch,不依赖 SSE 保存业务状态或私有正文。模型事件只提示 config set version 或 runtime availability version 变化,客户端重新读取 `/models`,不得把运行时不可用写回配置表。 + +`GET /models` 顶层固定返回 `configured_default_model_id`、`recommended_model_id`(无运行时可用模型时为 null)和当前 `config_set_version`。每个模型固定返回 `enabled`、`is_default`、`recommendation_priority`、`config_version`、成本/限制、`contract_validation_status`,以及独立的 `runtime_availability={available_for_new_jobs, reason, checked_at}`。`recommended_model_id` 只从 enabled、contract verified、Worker/网关可用于新任务的模型中按 recommendation_priority 数值升序派生;它不形成第二个默认状态,也不更新任何 ModelConfig 字段。 + +`POST /generations` 的业务字段固定为 prompt、model_id、ratio、model_config_version、confirmed_credit_cost、创建模式(new_project 或 existing_project)以及参考图项;不接受负面提示词、种子、采样器、批量数量或模型专属高级参数。参考图项只能是本次 multipart 新文件,或同一所有者且同一项目中已有任务的 `existing_reference_asset_id`;复用时仍创建当前 GenerationJob 的关系快照,不能修改旧任务关系。 + +### 6.3 鉴权与会话 + +- 会话令牌为 256 位随机 opaque 值;数据库只保存 SHA-256 摘要、用户、创建、到期和撤销时间。 +- Cookie 固定为 HttpOnly、SameSite=Strict、Path=/,最长 30 天。因 P0-A 使用回环 HTTP,不把 Secure Cookie 当作安全前提。 +- 每次页面加载通过会话接口取得一次性 CSRF synchronizer token,仅保存在页面内存;所有写请求同时校验 CSRF、Origin、Host 和会话。 +- 用户暂停、注销、管理员停用或移出白名单时,在同一事务撤销全部会话;API 每次请求重新检查账号状态和管理员白名单配置版本。 +- 管理员和普通用户使用分离入口与会话 audience;普通账号不能因邮箱加入白名单原地提升。 +- `user_profiles.private_content_notice_version` 与确认时间是后端真相;Cookie、session、localStorage、IndexedDB 或页面内存只能缓存本次显示状态,不能作为确认依据。管理员 session/bootstrap 每次从当前后端告知版本和用户记录计算 `notice_acknowledged`。 + +### 6.4 HTTP 语义 + +- `400`:字段或确定性业务输入无效;`401`:无会话;`403`:角色、所有者或预览授权不足;`404`:不存在或对当前主体不可见。 +- `409`:幂等键请求哈希冲突、活动项目上限、历史上限或不可执行状态;已有活动 GenerationJob 返回 `200` 和现有任务引用。 +- `412`:`state_version` 或 `model_config_version` 过期;响应带最新安全版本摘要,客户端必须重新确认。 +- `413`:参考图或后台贴纸上传超过已验证限制。 +- `426`:浏览器品牌、平台或发布 major 不受支持,或无法可靠识别;只返回阻断引导所需字段。 +- `428`:active super_admin 尚未确认当前私有内容告知版本;不返回私有内容区域数据。 +- `429`:验证码频率限制;配额硬停止使用服务状态响应而不是伪造发送成功。 +- `503`:外部服务 paused、网关契约未验证、Worker degraded;生成类响应附固定 error_category(适用时)。 +- `507`:当路由要求新增受管内容且 LocalBackendStorageState 为 full/unavailable,或本次预计写入后将超过硬上限时返回;响应带非敏感容量状态和 remaining bytes。预计总量恰好等于硬上限不得以 507 拒绝。full 不影响已有项目状态保存,unavailable 时才拒绝该保存;查看、下载、客户端仅下载导出和删除按 4.4 节处理。 + +### 6.5 版本用途 + +- `config_version`:ModelConfig 每次影响启停、默认、recommendation_priority、成本、比例、参考图、提示词、路由或错误映射时生成新不可变版本;三个当前版本由一个 `config_set_version` 原子激活。生成提交必须同时带 `model_config_version` 和 `confirmed_credit_cost`。 +- `state_version`:每次项目保存成功递增。保存请求使用 `If-Match`;不匹配进入 conflicted,只读且不能另存副本。 +- `release_version`:每次素材发布生成不可变版本;项目元素保存稳定 ID 和 resource_version,历史渲染不跟随新版本漂移。 +- `schema_version`:数据库迁移版本;应用启动只允许向前执行已打包迁移。 +- `app_version`:发布包版本;写入 RELEASE.json、日志和诊断,但不改变产品数据语义。 + +### 6.6 新增稳定错误码 + +| code | HTTP | 触发与固定动作 | +| --- | ---: | --- | +| `BROWSER_UNSUPPORTED` | 426 | 品牌/版本/平台不支持或身份不可可靠识别;只显示无绕过引导 | +| `MODEL_CONFIG_VERSION_CONFLICT` | 412 | 后台提交的 expected config_set_version 已过期;返回最新非敏感版本摘要并要求重载 | +| `MODEL_DEFAULT_REPLACEMENT_REQUIRED` | 409 | 试图停用当前默认模型但未在同一请求指定另一个 enabled 默认;整体不写入 | +| `MODEL_DEFAULT_REPLACEMENT_INVALID` | 409 | 替代默认不存在、未启用或产生非唯一默认;整体不写入 | +| `MODEL_RECOMMENDATION_PRIORITY_INVALID` | 400 | 缺失、非整数或非正数;返回字段位置,整体不写入 | +| `MODEL_RECOMMENDATION_PRIORITY_CONFLICT` | 409 | 同一候选配置集合内优先级重复;返回冲突模型 ID,不部分写入 | +| `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED` | 428 | 当前 super_admin 未确认当前告知版本;仅返回 current notice version/message_key | +| `ASSET_HISTORY_REFERENCE_CONFLICT` | 409 | 清理确认时任一文件仍被已发布版本、active/trashed 项目或其他有效关系引用;整批不排队 | +| `ASSET_CLEANUP_CANDIDATE_STALE` | 409 | 候选快照/确认令牌过期或文件状态已变化;重新查询候选 | +| `STORAGE_CAPACITY_EXCEEDED` | 507 | 当前已 full/unavailable 或预计写入后大于硬上限;不得创建副作用,等于上限不触发 | + +上述错误均使用统一错误信封和稳定 message_key,不新增 GenerationErrorCategory。模型因余额、契约或 Worker 状态不可用于新任务时,生成提交继续使用九类错误中的既有分类;模型读取接口只报告独立运行时原因。 + +## 7. 幂等、并发与事务 + +### 7.1 幂等规则 + +- 相同主体、路由和 `Idempotency-Key` 且请求哈希一致时返回第一次结果;哈希不一致返回 409。 +- GenerationJob 的 `client_submission_id`、点数流水 `operation_key`、管理员调点 `adjustment_id`、邀请码核销 `registration_id` 和 export `export_id` 具有永久唯一约束。 +- 普通 API 幂等响应缓存保留 24 小时;涉及任务、点数、邀请核销和管理员操作的实体唯一键随实体/审计保留,不依赖 24 小时缓存。 +- Worker 结算使用 `generation_id + final_credit_state` 唯一约束,重复回调、轮询或进程恢复不能再次 commit/release。 + +### 7.2 单任务约束 + +`generation_jobs` 保存 `owner_id`,并建立 SQLite 部分唯一索引:仅当 status 为 queued 或 running 时,owner_id 唯一。API 在 `BEGIN IMMEDIATE` 事务中先查询已有任务,再尝试插入;即使并发请求同时到达,索引仍保证只存在一个活动任务。命中已有任务时返回该任务,不新增项目、参考图关系、冻结点或上游调用。 + +### 7.3 生成提交事务 + +1. 校验会话、账号、项目/历史上限、当前 config set、所选模型 enabled、contract verified、独立 runtime availability、配置版本、比例、提示词、参考图、余额、Worker 健康和存储状态;recommended_model_id 仅用于默认推荐,不替代对用户所选模型的校验。 +2. 流式接收并校验参考图到 staging,建立预计容量预留。 +3. 在一个 `BEGIN IMMEDIATE` 中再次读取所有可变条件;需要时创建草稿 Project;创建 GenerationJob 和参考图关系;把 available 转入 reserved;写 generation_reserve 流水和 outbox。 +4. 提交后移动参考图到私有内容位置并登记 managed file。任一步失败则事务不创建任务、不冻结点;已暂存文件进入补偿清理。 +5. Worker 只处理已完整提交的 queued 任务。 + +### 7.4 结算事务 + +- 成功:验证图片、写入受管文件后,在同一事务把任务改为 succeeded、写 output_asset、把 reserved 变为已扣点、追加 generation_commit、更新项目历史与 draft_state。 +- 失败:在同一事务写 failed/rejected 和固定 error_category、把 reserved 返还 available、追加 generation_release;输出文件不得建立引用。 +- 供应商返回成功但文件不能安全落盘时,不把任务标为 succeeded;使用 `unknown_retryable` 失败并只释放一次,后台保留非敏感诊断类别。 +- 管理员调点只更新 available;原因必填;`reserved_balance` 不变;流水和 AdminOperationLog 与余额更新同事务提交。 + +### 7.5 项目保存并发 + +- 前端在一次已提交操作结束后 1 秒 debounce;同项目最多一个保存请求在途,新变化合并为下一份完整状态。 +- API 使用 `UPDATE ... WHERE project_id=? AND owner_id=? AND state_version=?` 原子 compare-and-swap。 +- 更新成功时完整 JSON Schema 校验、state_version +1、save_status=saved;失败返回 412,前端立即停止自动保存并进入只读 conflicted。 +- conflicted 页面把 `conflict_export_used` 只保存在当前页面内存;第一次本地导出结束后立即禁用再次导出,且始终不调用 latest export API。 +- 未保存状态、撤销栈、提示词和私有引用只在页面内存;刷新、关闭或崩溃后只恢复最后一次 saved 状态。 + +### 7.6 ModelConfig 原子配置与运行时推荐 + +`model_config_sets` 是工程事务容器,不新增产品对象。每次后台保存都生成一个候选 set,把三个模型各自新的或复用的 immutable config_version 作为 set 成员;Schema 与数据库固定执行: + +- `recommendation_priority` 为 JSON/TypeBox integer,数据库 `CHECK (recommendation_priority > 0)`,并以 `UNIQUE(config_set_id, recommendation_priority)` 禁止并列;缺失、浮点、零、负数或重复均拒绝整个请求。 +- `UNIQUE(config_set_id) WHERE enabled=1 AND is_default=1` 保证至多一个;`model_config_current` 指针更新 trigger 在切换前检查候选 set 中 enabled default 计数恰好为 1,否则中止事务。因此任何可读取的当前配置始终恰好有一个已启用默认模型。 +- `PUT /admin/models/configuration` 必须提交完整三模型集合、`expected_config_set_version` 和 Idempotency-Key。API 在 `BEGIN IMMEDIATE` 中复核当前版本、模型 ID 固定集合、默认模型、优先级、字段约束和契约重验规则,写 immutable 版本、AdminOperationLog/outbox,最后单语句切换 current set 指针;契约相关字段变化时同一事务把对应 runtime availability 置 false/reason=contract_unverified。任一失败回滚全部写入。 +- 当前默认由 enabled 变为 disabled 时,请求中的另一个模型必须同时为 enabled + is_default;缺失或非法分别返回固定 replacement 错误。该事务不得先产生无默认或双默认的可见中间态。 +- `model_runtime_availability` 不属于 config set。余额、契约、Worker 或外部服务变化只在独立事务更新逐模型运行时可用性、GatewayBalanceState/审计和 runtime version,不创建 config_version,也不修改 enabled、is_default 或 recommendation_priority。 +- 契约相关配置变化仍按 PRD 生成新 config_version 并把 `contract_validation_status` 置为 unverified,但新版本必须继承候选集合明确提交的 enabled/is_default/recommendation_priority;契约失效本身不得暗改这三个字段,只通过 runtime availability 禁止新任务。 +- 逐模型 runtime reason 固定为 `available`、`configured_disabled`、`contract_unverified`、`contract_blocked`、`gateway_balance_insufficient`、`gateway_paused` 或 `worker_degraded`;读取 API 不透传供应商文本。多个原因同时存在时按 configured disabled、contract、balance/paused、Worker 的顺序返回首要阻断原因,恢复一个原因后仍需重新计算其余原因。 +- 读取时先确定当前 config set,再对每个 enabled 模型联结 runtime availability;`recommended_model_id` 按有效候选的 recommendation_priority 升序取第一项,无候选返回 null。configured default 即使运行时不可用仍保持原 ID,前端不得把推荐结果写回后台默认。 + +初始化迁移种入固定三模型集合且三者 configured enabled=true:`gemini-3.1-flash-image-preview` 为 `is_default=true`、`recommendation_priority=1`;`gemini-3-pro-image-preview` 为 priority 2;`gpt-image-2` 为 priority 3。三者初始 contract 为 unverified、运行时不可用于真实任务;初始化和后续迁移都通过同一集合校验器,禁止专用旁路。 + +### 7.7 后台贴纸历史文件显式清理 + +项目保存事务从 Canvas Schema 提取普通贴纸 stable ID/resource_version,重建 `project_asset_refs`;active 和 trashed 项目均保留关系,只有项目进入 purged 后才撤销。`asset_release_items` 对所有仍保留的 immutable 已发布版本保留原图/缩略图关系。候选与清理流程固定为: + +1. 候选查询只选择 `asset_origin=admin_uploaded`、已不再供当前新增使用,且不存在任何 `asset_release_items`、active/trashed `project_asset_refs`、待发布集合或其他有效引用的原图/缩略图;返回内部 file ID、stable sticker ID、resource_version、file_kind、bytes、hash prefix、引用计数零和短期 `candidate_snapshot_version`,不返回路径。 +2. super_admin 以候选快照创建 cleanup intent;API 要求 Idempotency-Key,写入请求及候选摘要的 immutable AdminOperationLog,并返回必须由同一 active super_admin 明确确认的短期确认令牌。查询候选本身不删除、不排队。 +3. 确认接口在 `BEGIN IMMEDIATE` 中锁定 intent 和候选文件,重新检查管理员、快照、文件状态及全部引用。任一文件被任一已发布版本、active 项目、尚可恢复的 trashed 项目、待发布集合或其他有效关系引用时,整批不排队,提交 `result=denied` 的引用复核审计后返回 `ASSET_HISTORY_REFERENCE_CONFLICT`;快照失效返回 stale 错误并要求重查。 +4. 全部无引用时,同一事务把文件置 `pending_delete`、撤销新的目录读取、写入 `file_cleanup_queue`/outbox,并记录 `validated` 与 `scheduled` 审计。排队不减少 `managed_content_bytes`,也不提前改变 LocalBackendStorageState。 +5. Worker 按 operation/file 唯一键执行物理删除并可幂等重试。每轮完成后重新扫描受管文件实际字节,在单一事务更新文件物理结果、`managed_content_bytes`、notice/status/last_measured_at 和最终 AdminOperationLog;失败或部分完成记录非敏感计数并继续重试,绝不把未物理删除的字节扣除。 +6. 只有重计量确认容量低于硬上限且 LocalDataRoot 可写时才恢复 active。系统、容量告警、发布或保留期作业均不得自动创建贴纸历史 cleanup intent。 + +full 不阻止候选查询、确认和排队。unavailable 时仍可查询;只有 SQLite 可写时才能确认并排队,SQLite 不可写则明确返回操作未提交,恢复后重试。两种状态都不得借清理接口上传替换文件、生成新缩略图或写入其他受管二进制。 + +### 7.8 私有内容告知确认与逐次访问 + +当前私有内容告知版本作为编译期非敏感业务配置随 app_version 发布,包含稳定 version、message_key、content SHA-256 和 effective_at;修改文案必须递增 version。`POST /admin/private-content-notice/ack` 要求 active super_admin、CSRF、Idempotency-Key 和客户端看到的 expected notice version,在 `BEGIN IMMEDIATE` 中重新比较当前版本并更新该 super_admin 的 UserProfile 两个字段;版本过期时不写入并要求重新读取。 + +任何 `/admin/private-content/*` 区域数据接口都在服务端比较 UserProfile 已确认版本与当前版本;不相等先返回 `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED`,不能用 Cookie、session 或浏览器存储绕过。图片/完整提示词打开接口在同一数据库事务再次检查 active super_admin 和当前告知确认,先插入 PrivateContentAccessLog 并提交,随后才发放一次受控读取;审计写入失败时不返回内容。告知升版会使所有旧确认自然失效,但确认记录绝不代替任何一次 PrivateContentAccessLog。 + +## 8. Generation Worker 与三模型适配 + +### 8.1 持久队列 + +Worker 使用 SQLite 原子领取 queued 任务并写入 `lease_owner`、`lease_expires_at`、`heartbeat_at` 和 `attempt_no`,任务转为 running。租约 30 秒,心跳每 10 秒更新;一次只有一个本机 Worker 实例持有命名锁。 + +Worker 不对供应商请求做自动重新提交。超时、连接中断或未知结果进入相应固定错误分类,由用户决定是否重试;这避免恢复时重复产生真实调用和扣费。 + +### 8.2 固定 Adapter 接口 + +三个模型各有独立模块,均实现: + +- `validateContract(routeProfile)`:检查当前配置引用已验证证据; +- `start(requestSnapshot)`:返回 completed 或 pending,pending 必须包含不含凭据的 upstream job reference; +- `poll(upstreamJobReference)`:只用于契约已经确认的异步模型; +- `normalizeOutput(response)`:输出统一图片流、MIME、像素、大小和供应商用量摘要; +- `classifyError(error, mappingProfile)`:只返回九类固定分类和非敏感 source category; +- `checkBalanceSignal(response)`:更新 GatewayBalanceState 的模型、账户或未知影响范围。 + +接口允许封装同步或异步供应商差异,但每个 Adapter 必须依据自身证据实现,不能复制另一个模型的字段或端点。每次请求固定一张输出;Adapter 按已验证协议发送单图约束,网关返回多图或结构不符时不得静默选取,按 gateway_contract_invalid 处理并使该配置重新进入 unverified。 + +初始数据通过 7.6 的配置集合迁移种入三个 PRD 模型 ID且三者 configured `enabled=true`:`gemini-3.1-flash-image-preview` 为唯一默认且 recommendation_priority=1,`gemini-3-pro-image-preview` 为 priority 2,`gpt-image-2` 为 priority 3,三者 `credit_cost=1`。在契约证据通过前 route profile 不填猜测字段、`contract_validation_status=unverified`、`available_for_new_jobs=false`;configured enabled 与运行时可用字段分离,验证通过并保存证据后才能恢复运行时真实任务能力。 + +### 8.3 契约验证证据 + +每个模型的验证记录必须包含: + +- 网关文档或平台支持确认的版本/日期; +- 脱敏请求与响应 fixture; +- 纯文字单图、参考图、四种比例; +- 返回 MIME、像素、获取方式和 URL 有效期; +- 同步/异步、任务查询、超时和取消/未知结果语义; +- 技术失败、安全拒绝、非安全参考图无效、余额不足和未知错误; +- error mapping profile 版本和 config_version; +- 证据 SHA-256、验证人和验证时间,不包含密钥、完整私有提示词或真实用户图片。 + +公开模型广场显示的端点与通用参数表只可作为验证输入,不可写成 verified 证据。任一端点、字段、参考图、响应、异步查询或错误协议改变,保存配置时立即将对应模型设为 unverified,停止其新任务并重新执行 AC-40。 + +`contract_validation_status` 初始和配置变更后为 unverified;完整矩阵通过后为 verified;平台确认不支持 PRD 必需能力或验证存在无法继续的外部条件时为 blocked。unverified 和 blocked 均禁止真实新任务并返回 gateway_contract_invalid,只有新证据完整通过后才能转为 verified。 + +### 8.4 崩溃恢复 + +- 启动时 Worker 扫描租约过期 running 任务。 +- 若任务已有经验证可查询的 upstream reference,只恢复 poll,不重新 submit。 +- `upstream_outcome_known` 初始为 false;Adapter 取得可验证终态时设为 true。`upstream_cost_reconciliation` 固定使用 `not_applicable`、`not_required`、`pending_manual_review`、`confirmed_charged` 或 `confirmed_not_charged`;该值不改变 Dada 点数处理。 +- 若已向上游提交但没有可安全查询的 upstream reference,任务以 `unknown_retryable` 失败并释放冻结点,不推测上游结果,也不自动重试;同时保持 `upstream_outcome_known=false`、设 `upstream_cost_reconciliation=pending_manual_review` 并记录非敏感提交时间/路由引用,供管理员核查网关实际消耗。 +- 管理员核对只能将 pending 转为 confirmed_charged 或 confirmed_not_charged 并写 AdminOperationLog;不得因核对结果补扣用户点数、改写任务终态或自动重新提交。 +- queued 任务可以由新 Worker 正常领取;页面关闭不改变其状态和点数。 +- 清理/结算 outbox 使用实体唯一键重放,保证最终只执行一次。 + +### 8.5 九类错误统一路径 + +| category | 创建前 | 创建后终态 | 点数 | 工程处理 | +| --- | --- | --- | --- | --- | +| upstream_timeout | 不适用 | failed | release | Adapter 超时映射;允许原输入重试 | +| upstream_failed | 不适用 | failed | release | 已知临时上游故障;稍后重试 | +| safety_rejected | 不适用 | rejected | release | 仅供应商安全拒绝;要求修改提示词或参考图 | +| model_disabled | 不创建 | 不适用 | no reserve | 配置 enabled=false;余额/契约/Worker 运行时原因由更具体分类优先 | +| gateway_balance_insufficient | 不创建或 failed | failed(若已创建) | no reserve/release | 更新独立运行时影响范围,不改配置;人工确认恢复 | +| gateway_contract_invalid | 不创建或 failed | failed(若已创建) | no reserve/release | contract 非 verified;停止运行时路由但不改配置默认/优先级,要求重新验证 | +| reference_invalid | 不创建或 failed | failed(若已创建) | no reserve/release | 格式、大小、数量或非安全型内容不可用;不得映射为 safety_rejected | +| unknown_retryable | 不适用 | failed | release | 无法细分但可稍后重试;保留非敏感诊断 | +| unknown_non_retryable | 不适用 | failed | release | 不应反复重试;联系管理员 | + +点数不足、配置版本变化、已有任务、项目/历史上限和本机 storage full/unavailable 使用确定性业务错误,不扩充 GenerationErrorCategory。 + +## 9. 核心业务工程方案 + +### 9.1 邀请注册、验证码、会话和管理员 + +- 注册发码前和验证码成功创建账号前均在事务中重新校验邀请码、账号状态和 10 人名额。 +- 邀请码由密码学安全随机数生成,仅在创建成功响应中显示一次;数据库保存带 pepper 的 HMAC、最大次数、有效期、状态和计数,不保存可再次读取的原码,日志与审计只保存邀请码内部 ID。 +- `email_challenges` 只保存验证码 HMAC、用途、过期时间、失败次数和 consumed_at;10 分钟过期、60 秒重发限制;连续失败和高频请求按邮箱摘要与本机会话组合限流。 +- Resend 用量领取与 `used_count` 更新使用 `BEGIN IMMEDIATE`。调用尝试一经放行即计数,供应商超时不回退计数,以保证不突破免费硬上限。 +- 账号创建、初始 10 点、registration_grant、邀请码使用次数和隐私告知版本同事务完成;失败不消耗邀请码。 +- active 与 suspended 普通用户计入 10 人;deleted 释放;super_admin 不计入且不使用邀请码。 +- 管理员登录使用独立入口。白名单外邮箱在调用 Resend 前拒绝;允许多个权限相同的 super_admin,不设置最后一个管理员保护。 +- 开发初始化脚本只写入 P0A-dev 数据根并在启动 banner 标识 DEV_SEEDED_ACCOUNT;发布构建不暴露入口,正式认证 AC 不可使用该账号作为证据。 + +### 9.2 项目、历史、失败草稿和回收站 + +- “新建创作”首次提交创建草稿项目;失败保留 `failed_empty`;重试复用项目并新增任务;“继续生成”只追加任务。 +- 默认项目名由 API 在创建事务内一次生成:将首次提示词做 Unicode 空白折叠,取前 24 个字素簇作摘要(空摘要使用固定 `未命名创作`),追加按当前 Windows 时区计算的 `YYYY-MM-DD`。同名允许,身份由 project_id 确定;用户重命名作为普通自动保存操作。 +- 项目固定 ratio/size;最多 20 个 active,成功历史最多 10 张。恢复 trashed 项目同样事务检查 active 上限。 +- 仅无成功图的 failed_empty 可进入批量移入回收站命令;API 根据数据库状态判断,不能相信客户端 batch_deletable。 +- 当前底图不能直接删除;切换底图保留覆盖元素、重置底图调整、重新提取已有色卡,并形成一次撤销/保存操作。 +- 自动保存由页面内单串行队列执行:已提交操作后 1 秒 debounce,同时最多一个保存请求,期间新变化合并为下一个最新快照。失败保持 failed/dirty 并在页面存活时指数退避重试,不写浏览器持久存储。 +- 站内离开守卫对 dirty/saving/failed 固定提供保存并离开、放弃修改和取消;保存并离开必须等待 saved,失败则不跳转。刷新/关闭使用 `beforeunload` 并发起最后一次 best-effort 保存;放弃、强制刷新、关闭或崩溃只能恢复最后 saved 版本。 +- trashed 精确保存 deleted_at/purge_at;到期或主动永久删除后先变 purged 并立即撤销访问,再进入物理清理队列。 + +### 9.3 画布状态与渲染 + +- Dada Canvas Schema 保存背景、调整、元素、资源版本和 z_index;加载时转换为 Fabric 对象,保存前再规范化,禁止持久化 Fabric 缓存、DOM 或函数。 +- 复杂资源统一编译为 `TemplateRenderModel v1`,由 TypeScript Canvas 2D/Fabric renderer 消费;文字运行、装饰图片、参数槽和动态字段均为声明式节点,不使用 HTML/CSS 布局作为导出真相。编辑预览与导出调用同一 renderer 和资源版本。 +- 画布最多 50 个覆盖元素;服务端和客户端同时校验。 +- 选择循环、长按候选、Shift/多选模式、框选、层级、吸附、撤销重做由编辑域控制器实现;撤销栈仅页面内存,保存的是撤销完成后的当前状态。 +- 字体加载必须等待 `FontFace` ready 后才能测量、生成基准图或导出。字体缺失使对应模板 unavailable;DYN012 明确使用 FONT081,不允许静默系统字体替代。 +- 切换文字模板只替换模板资源和模板默认样式,保留用户文本、换行、位置、缩放、旋转和层级;字体覆盖及填充/描边/背景/对齐/行距/字距写入 CanvasElement 的显式样式字段。 +- 底图调整使用确定性渲染参数,原图与显示调整分离;色卡算法始终读取未裁剪、未调色、未滤镜的原始底图像素。原图按保持比例缩放到最长边 256px 的离屏 Canvas,固定逐像素扫描,使用 MMCQ 生成五色;按像素占比降序、RGB 数值升序打破并列,并把 `palette_algorithm_version=mmcq-v1` 保存到色卡元素,确保同图同版本结果一致。 +- 动态贴纸由固定 TypeScript provider 生成声明式元素。时间取插入时本机值并保存;身份默认复制插入当时的账号资料到 DynamicStickerData,单实例覆盖只改该元素,账号资料后续变化不暗改已保存画布。需要 `@` 的 provider 先去掉用户值前导 `@` 再添加一个;DYN004 在确认后才调用定位并保存原始坐标。 + +### 9.4 素材面板与公开缓存 + +- 普通贴纸目录按 part 和稳定顺序分页;前端虚拟列表只保留视口与小缓冲,缩略图懒加载,原图只在加入画布或导出时加载。 +- 文字模板只按 catalog 分类、稳定展示顺序和 display_name 搜索;普通贴纸只按 part 浏览,不复用文字搜索索引,也不从哈希文件名推断关键词。 +- 最近使用只保存账号、稳定 ID、资源版本和时间,不保存项目内容。 +- 公开二进制使用版本化 URL、ETag 和 `Cache-Control: public, max-age=31536000, immutable`。 +- 应用主动缓存使用 Cache Storage,但 allowlist 仅包含 public_release_asset 中的公开缩略图、公开模板转换产物和公开字体;普通贴纸原图、生成图、成品和其他 public response 不进入 Dada 主动持久缓存。IndexedDB 只保存上述 allowlist 资源的 resource ID、release_version、bytes、last_accessed_at。写入前按 LRU 淘汰,确保不超过 157,286,400 字节。 +- internal preview 和 private response 使用 `Cache-Control: no-store`,Service Worker 不拦截;未保存状态不得进入任何浏览器持久化存储。 + +### 9.5 客户端导出与 latest exports + +- 导出由浏览器按项目真实像素在离屏 Canvas 合成;PNG 无损,JPG 默认 92 且仅允许 80-100;颜色空间固定 sRGB,无水印。 +- 未提交文字/样式在用户确认后先形成一个撤销操作,等待字体与图片稳定,再执行一次合成。 +- 同一 Blob 一路用于浏览器下载和后端 latest export 上传,避免下载与保存结果不同。 +- 上传携带 project_id、state_version、format、SHA-256、像素和 `export_id`;API 校验所有者、格式、像素和容量后原子替换该格式最新文件。 +- 项目 conflicted、项目 JSON 保存失败、latest export 持久化失败或 storage unavailable 时仍允许客户端下载,但不更新项目或 latest_exports。storage full 下仍按 4.3/4.4 节允许纯 JSON 项目保存,只禁止 latest export 上传;旧成功成品保持不变。 +- 从未导出的项目返回稳定空状态,不自动生成默认成品;P0-A 只提供本机重新下载和原始生成图下载。 + +### 9.6 运营后台与两类审计 + +- 后台使用同一 React 应用的独立 `/admin` 路由域和独立 API 权限中间件,不建立第二套服务。 +- 管理员 session/bootstrap 返回当前私有内容告知 version/message_key、该身份已确认 version/time 和计算后的 `notice_acknowledged`。私有内容区域数据路由在服务端强制当前版本确认;版本升高后重新阻断,确认不使用浏览器或会话状态代替。 +- 列表默认只返回非敏感摘要。完整提示词/图片必须调用专用 private access API;API 再次校验当前告知确认,先插入 PrivateContentAccessLog,再返回资源。告知确认不能替代逐次审计。 +- 管理员修改用户、点数、邀请码、模型、普通贴纸、复杂模板、预览授权、辅助服务或余额恢复,以及创建/确认/执行贴纸历史清理时,在对应业务事务内写 AdminOperationLog。 +- before/after summary 只保存枚举、数值、版本和内部对象引用;不保存正文、图片、凭据或完整白名单。 +- 后台不能编辑复杂模板内部样式、固定错误分类、服务硬上限上调、自动充值或付费通道。 + +### 9.7 内部预览授权 + +- 公开 manifest 在构建和响应时双重过滤,只能含 public_release_asset。 +- 内部预览 manifest 每次按 active session、active user、grant 状态、有效期和 test_batch_id 动态生成,资源 URL 使用随机 manifest item ID,不能由模板稳定 ID 推导。 +- 撤销、到期或暂停账号后立即拒绝后续 manifest/资源请求;已经取得的 response 为 no-store,不进入应用缓存。 +- full_p0 可以分批 imported/internal_preview/passed,但后台不提供逐批普通用户 enabled 操作。未来正式切换必须是一次事务化发布命令,并且只在 P0-B 全部门槛通过后存在。 + +## 10. 素材工程方案 + +### 10.1 唯一输入 + +素材编译器只读取 PRD 18.1-18.7 的规范入口: + +- `%USERPROFILE%\Desktop\sticker_web_handoff` 的交接说明、总 manifest 和完整性报告; +- 文字模板分类 `catalog.csv` 与 `templates//metadata.json`; +- 字体 `font_panel_catalog.csv` 与单字体 metadata; +- `%USERPROFILE%\Desktop\贴纸素材\sticker_part1` 至 `sticker_part25` 的 1,407 张 PNG; +- 色卡 `catalog.csv` 与 `styles//metadata.json`; +- 动态贴纸 `catalog.csv` 与 `templates//metadata.json`。 + +历史 PB、截图、录像和对账目录只作人工证据,不进入产品构建或运行时。编译器以只读句柄打开源文件,不修正、删除、重命名或生成旁路文件。 + +### 10.2 StaticStickerCatalog 构建 + +1. 按 part1-25 和目录稳定规则枚举 1,407 张 PNG,重复内容不去重目录项。 +2. 为每个入口生成稳定 ID、part、顺序、原文件名、相对路径、宽高、MIME、SHA-256 和缩略图引用。 +3. 校验计数、PNG 解码、路径唯一、ID 唯一和 manifest 哈希;任一失败阻止发布。 +4. Git 保存 catalog、metadata、manifest 和校验报告;PNG 原图仍在源目录原地只读,通过后端 ID 路由提供。 + +### 10.3 复杂资源包 + +- 文字、字体、色卡和动态贴纸由 TypeScript 编译器转换为版本化声明式 JSON、字体/图片引用和必要派生文件。 +- Lua/Prefab 仅作为转换输入和行为证据;编译器不执行脚本,浏览器和后端运行时也不解释或执行 Lua/Prefab。 +- 332 个文字模板、86 个字体面板项、16 个色卡和 35 个动态贴纸全部完成导入和注册;P0-A 普通用户 manifest 只导出 32 个文字白名单、其引用字体并强制 FONT081、4 个色卡和 10 个动态贴纸。色卡和动态贴纸不计入普通贴纸的 25 个 part。 +- 浏览器或后端无法从规范源直接安全服务的最小转换产物写入 `derived-assets//` 并计入 LocalBackendStorageState。编译器先查找已有内容哈希并复用;源二进制、可直接服务文件和未被 P0-A/内部预览 release 引用的预转换产物不复制或生成。 + +### 10.4 后台新增普通贴纸 + +管理员上传 PNG/WebP 后,API 分别为原图和 Dada 生成缩略图建立容量预留,流式写 staging,验证真实 MIME、解码、尺寸、大小、稳定 ID、part 和顺序;通过后创建新的 immutable release_version,将两类文件作为 `sticker_original`/`sticker_thumbnail` 写入 `managed-assets` 并计入 managed_content_bytes,catalog、metadata 和 manifest 只写版本化元数据。此路由在 storage full/unavailable 时拒绝上传和缩略图生成;预计总量大于上限时拒绝,恰好等于上限时允许提交并进入 full。不得把既有规范归档导入 `managed-assets`。发布事务切换 current release,旧版本只读保留;历史文件只能按 7.7 的候选、确认、引用复核、审计、队列、物理删除和重测流程释放。 + +后台不能上传 Lua、Prefab、文字模板、色卡渲染器或动态资源包。复杂资源只能走受控离线编译和版本发布。 + +### 10.5 Manifest 与发布路径 + +- `release_version` 使用 `asset-YYYYMMDD.`;每个 manifest 自身和每个文件均有 SHA-256。 +- 只读源对象键为 `root_ref + relative_path`;LocalDataRoot 派生对象键为 `release_version/content_hash`;浏览器只看到稳定资源 ID 和版本化 API URL。 +- manifest 一经发布不可修改;修正生成新版本。项目保存稳定 ID + resource_version,旧项目始终按旧版本渲染。 +- Gitea CI 对 catalog/schema/hash 使用 fixture 验证;受控 release job 在当前电脑只读校验真实归档并生成 manifest,不将 3.57 GB 二进制或其等价派生副本上传为 CI 制品。 + +### 10.6 三类资源隔离 + +| access_class | API | 鉴权 | 缓存 | +| --- | --- | --- | --- | +| public_release_asset | `/api/v1/assets/public/...` | 先通过浏览器 support gate;无用户会话要求;只允许 manifest 白名单资源 | 仅公开缩略图、转换产物和字体可进入 150 MB LRU;immutable | +| internal_preview_asset | `/api/v1/assets/preview/...` | active 普通账号 + 每次校验 AssetPreviewGrant | `no-store`;不得进入公开 manifest | +| private_user_asset | `/api/v1/private-assets/...` | 所有者,或先写审计的 active super_admin | `private, no-store`;不可推导路径 | + +### 10.7 素材授权边界 + +P0-A 只维护规范归档中已有的素材来源引用、manifest、catalog、metadata、版本和 SHA-256,并保持受控内测、三类资源隔离和不自行扩大公开范围。完整商业授权台账不新增为当前 P0-A 或本版已定义 P0-B 的技术/产品完成与发布门槛;若未来进入正式公开注册或商业化,则必须按届时适用的合规发布要求补齐。WP-5 主责 PRIV-05 不得被拆解为当前建设商业授权系统的任务。 + +## 11. 外部服务硬停止与恢复 + +### 11.1 Resend + +- `external_service_usage` 为每日 80 和自然月 2,400 分别建立计数行。事务领取调用额度;达到最后一个名额时先把服务置 paused_quota,再允许该次已领取调用结束。 +- paused_quota、paused_provider 或 disabled 时不调用供应商,不创建验证码假成功;注册和需要新验证码的登录暂停,现有有效会话继续。 +- 新免费周期开始时创建新计数行,但保持暂停,直到 super_admin 在后台确认周期和免费额度后执行恢复并审计。 +- 供应商恢复同样需要非敏感健康检查结果和 super_admin 人工恢复。免费额度下降只能下调 hard_limit 并审计;不能上调或切换付费通道。 +- QQ、163 和企业邮箱的发布前 20 封送达结果作为人工发布证据保存,不写入 ExternalServiceUsage,也不创建逐封 EmailVerificationEvent 或运行时质量状态。 + +### 11.2 高德 + +- 每自然月硬上限 1,000 次,配额领取与状态更新事务化;达到上限先 paused_quota。 +- paused 状态只关闭 DYN004 自动定位;手动地点文字和其他编辑功能继续。 +- 浏览器坐标只在 Dada 自有提示确认后取得,并由 API 发送高德;前端不持有高德密钥。 +- 新周期或供应商恢复后由 super_admin 人工恢复并写审计;不购买流量或自动扩容。 + +### 11.3 AI 网关余额 + +- Adapter 将非敏感余额信号写入 GatewayBalanceState。model 影响范围只停明确模型;account 或 unknown 停同 gateway_account_ref 全部模型。 +- 运行时停用事务只更新 `model_runtime_availability`、GatewayBalanceState 和 AdminOperationLog,并向 SSE 发布 runtime version;不得创建或改写 ModelConfig 版本、enabled、is_default 或 recommendation_priority。 +- 已创建失败任务只释放一次;Dada 点数不能覆盖真实网关余额。 +- 不自动探测后直接恢复。部署人员/管理员先确认真实余额,记录 confirmed recovery check,再由 super_admin 只恢复运行时可用状态并审计;恢复后重新派生 recommended_model_id,仍不回写配置默认或优先级。 + +## 12. 安全、隐私与保留 + +### 12.1 本机安全边界 + +- Fastify 只监听 `127.0.0.1`,拒绝其他 Host;Windows 防火墙不创建入站规则;不设置端口转发。 +- CSP 固定 `default-src 'self'`,图片/字体只允许同源 blob URL 和必要内存 URL;禁止第三方脚本。 +- AI 网关、Resend 和高德客户端只允许 HTTPS、正常校验 Windows 信任链与主机名、限制响应大小和超时,并拒绝重定向到未在固定供应商配置中的主机;任何环境都不得关闭证书验证。 +- 资源路由执行所有者/授权校验,404 隐藏对象是否存在;下载设置 `X-Content-Type-Options: nosniff` 和安全 Content-Disposition。 +- P0-A 不做 Dada 应用层文件加密,依赖当前 Windows 用户登录和 NTFS 权限;页面固定显示数据可丢失、不备份、不迁移提示。 + +### 12.2 账号注销 + +注销在一个事务中撤销会话、删除邮箱和资料、释放邮箱唯一约束、删除未使用点数、把项目/任务/资源标为不可访问、生成不含映射的匿名事件、匿名化私有访问日志目标并加入物理清理队列。事务提交后旧内容对任何用户都不可恢复;物理文件随后异步删除。 + +AnonymousRetainedEvent 最迟在注销后 180 天删除;AdminOperationLog 和 PrivateContentAccessLog 按原事件时间 180 天删除,注销不延长。不可回溯汇总只保存计数,不保留单次作品或主体键。 + +### 12.3 审计与敏感信息 + +- PrivateContentAccessLog 专用于打开完整提示词/图片;AdminOperationLog 专用于后台敏感操作,两者不重复保存正文。 +- 任何日志、错误信封、manifest、SSE、OpenAPI 示例和测试 fixture 都不得出现真实密钥、验证码、会话、管理员邮箱、完整提示词、私有图片或绝对用户路径。 +- Windows 凭据管理器读取只发生在子进程启动/重启时。Supervisor 通过继承的匿名管道按子进程最小权限传递一次,Node 在供应商客户端构造后清空接收缓冲;值不进入进程参数、环境变量、磁盘或日志。 +- CI 仅使用格式正确的假密钥和 `.invalid` 邮箱;正式验收凭据由离线命令写入。 + +## 13. 性能、可访问性与视觉一致性 + +### 13.1 P0-A 性能预算 + +在当前 Windows 电脑、当前稳定版 Chrome/Edge、1080p 以上视口、50 个混合覆盖元素的标准画布上: + +- 连续拖动/缩放/旋转 10 秒期间,指针到下一次画面更新的 p95 不超过 50 ms,单个主线程长任务不得超过 200 ms,不能出现连续 500 ms 无响应; +- 画布交互帧耗时 p95 不超过 33 ms;吸附和候选计算不得引起控件尺寸跳动; +- 自动保存序列化使用调度切片,单次主线程阻塞 p95 不超过 50 ms;网络/SQLite 保存不占用画布交互线程; +- 已缓存白名单资源的编辑器重新打开至可操作不超过 3 秒;未缓存资源显示稳定占位,不阻塞其他工具; +- 1080 x 1920、50 元素画布导出在 10 秒内完成,浏览器页面峰值额外内存不超过 1 GiB;失败不改变项目和 latest export; +- 普通贴纸面板 DOM 节点数不随 1,407 张总量线性增长;每个 part 只渲染视口和两屏缓冲。 + +性能测试使用固定数据集和采样脚本;未达到预算阻止对应 AC-27、AC-32 或发布门槛。本文不为 P0-B 手机定义当前实现预算。 + +### 13.2 视觉一致性 + +- Chrome/Edge 使用相同转换资产、字体文件、Canvas Schema、DPR 归一化和导出路径。 +- 基准运行固定 OS、浏览器、视口、DPR、字体 ready、动画时刻和动态数据;具体矩阵写入 tdd.md。 +- 元素边界偏差不超过 2px;颜色通道差异超过 16/255 的显著像素不超过总像素 1%;任一超限必须人工复核。 +- DYN012、style_02-16 和已知替代项无论自动差异是否通过均人工复核。 + +### 13.3 可访问性基础 + +- 所有关键命令、表单、对话框和画布外工具均可键盘到达;焦点不被托盘或 SSE 更新抢走。 +- 状态同时提供文字/图标/ARIA,不只依赖颜色;上传、任务、保存、冲突、存储和错误具有文字状态。 +- 浏览器不支持引导页必须在不加载产品 bundle 时仍具备语义标题、键盘可读说明和明确支持版本;WinForms 托盘、启动/端口/degraded 对话框和诊断结果使用 Windows UI Automation 可读名称、状态与动作。 +- CI 对关键页面运行 axe,critical/serious 问题为零;画布对象操作提供可聚焦的等价工具控件。最终焦点顺序、图标名称和文案由 UIDesign.md 固定。 + +## 14. 测试、监控与 AC 追踪 + +### 14.1 测试层级 + +| 层级 | 基础设施 | 覆盖目标 | +| --- | --- | --- | +| Schema/单元 | Vitest、TypeBox fixture | 状态转换、哈希、版本、九类错误、模型默认/优先级集合校验、运行时推荐、容量 `>`/`=` 边界、动态格式化、色卡确定性、路径校验 | +| SQLite 集成 | 每测试临时数据库和临时 LocalDataRoot | 事务、部分唯一索引、幂等、点数、名额、模型集合原子切换、告知确认持久化、审计不可变、贴纸引用竞争、清理和容量状态 | +| API 契约 | Fastify inject + OpenAPI snapshot | 浏览器门禁、分组路由、模型读取/保存、告知 ack、贴纸清理、稳定错误信封、鉴权、CSRF、If-Match、multipart、三类资源 | +| Worker 恢复 | 模拟网关、可控时钟、进程终止 harness | 租约、心跳、崩溃、poll 恢复、只结算/释放一次、运行时模型状态、上游结果/成本核对、贴纸物理删除/重测与清理重放 | +| 外部契约 | 三模型脱敏 fixture、Resend/Amap mock;受控真实验证命令 | 三模型分别验证;免费配额和错误分类;真实调用不在普通 CI | +| 浏览器 | Playwright Chrome/Edge 独立 profile + 原始 HTTP 客户端 | 发布记录中的真实 Chrome/Edge 通过;其他品牌、错误 major、缺失/冲突 UA-CH 和直调产品 API 均硬阻断;认证、项目、编辑、同机多标签、导出、越权和后台;Service Worker 不拦截 internal/private,Cache Storage/IndexedDB 只有 allowlist 公开资源元数据 | +| 安全/打包 | Windows 凭据测试 harness、进程检查、制品扫描 | 凭据缺失/清除、API/Worker 重启、最小权限传递;support gate 与辅助诊断最小字段;命令行、环境、文件、日志和崩溃诊断零真实凭据 | +| 视觉/性能 | 固定 Canvas fixture、截图 diff、PerformanceObserver | AC-32、50 元素、字体、动态模板、导出一致性 | +| 人工发布验收 | P0-A 便携包和真实外部依赖 | PRD 第 22、23 节适用门槛 | + +DevelopmentPlan 不定义逐场景操作步骤。tdd.md 必须从 `RELEASE.json` 接收并写定具体 Windows build、Chrome/Edge 品牌、major 与已验收 full version、DPR、视口、数据 fixture 和逐步断言;不得使用伪造 UA 字符串代替两种真实稳定浏览器的通过证据。 + +`reference_invalid` 必须分别覆盖格式/大小/数量和非安全型内容不可用;`safety_rejected` 必须使用供应商明确安全拒绝证据。二者不能通过同一个模拟错误或同一个断言替代。跨普通用户隔离验收使用独立 Chrome/Edge profile,通过正常邀请码、验证码和普通权限链路创建临时第二账号,完成越权检查后走正常注销并等待其内容清理;不得使用管理员直写、开发预置账号或隐藏测试后门替代。Playwright 每个缓存/私有资源场景后必须枚举 Service Worker registration、Cache Storage 键和 IndexedDB 记录,不能只依赖响应头断言。私有内容告知测试必须跨新标签页、API/浏览器重启、重新登录和另一受支持浏览器验证同版本不重复,并在告知 version 递增后验证服务端重新阻断;每次打开正文/图片仍单独断言 PrivateContentAccessLog。 + +### 14.2 本地监控 + +后台和诊断接口从 SQLite 聚合并显示: + +- GenerationJob queued/running 数、队列最老等待时间、租约过期数、各模型调用量/成功率/失败率/错误分类/耗时,以及 `pending_manual_review` 的上游成本核对数量和最老等待时间; +- configured_default_model_id、config_set_version、逐模型 recommendation_priority/runtime availability、recommended_model_id,以及无可用模型持续时间;运行时指标不得回写配置; +- 点数 reserve/commit/release 数和余额不变量异常; +- managed_content_bytes(分参考图、生成图、成品、派生文件、后台贴纸原图和缩略图)、阈值、storage_status、目录可写性、贴纸 cleanup intent/队列积压、引用冲突数、最近物理删除和重测时间; +- Resend 日/月用量、状态、硬上限和非敏感 pause reason; +- 高德月用量、状态和硬上限; +- GatewayBalanceState、影响范围、运行时不可用模型和最后确认时间; +- 审计到期清理、匿名事件清理、项目 purge、staging 清理和日志轮转结果; +- 素材 manifest 版本、根目录可用状态和校验失败数,不回显绝对路径。 + +固定本地告警条件:running 租约过期、队列等待超过 2 分钟、连续 5 个生成失败、无运行时可用模型、存在 `pending_manual_review` 上游成本核对、点数不变量失败、storage full/unavailable、贴纸或项目清理任务连续 3 次失败、外部服务 paused、网关余额不足/unknown、素材根校验失败、API/Worker degraded。告警进入后台和托盘状态,不发送外部告警或产生自动费用。 + +### 14.3 十三个功能模块落点 + +| FeatureSummary 模块 | 工程组件 | 主工作包 | +| --- | --- | --- | +| 邀请注册与邮箱登录 | Auth API、Resend adapter、session、admin allowlist | WP-1 | +| 点数与原子结算 | Credit service、ledger、SQLite 事务 | WP-2 | +| 三模型 AI 生图 | Generation API、ModelConfig set、runtime availability、Worker、三个 Adapter | WP-2 / WP-3 | +| 项目、历史、保存与冲突 | Project service、Canvas Schema、cleanup | WP-2 | +| 电脑画布与底图编辑 | React/Fabric editor | WP-4 | +| 文字模板与字体 | Asset compiler、Font loader、text renderer | WP-4 / WP-5 | +| 25 个普通贴纸 part | StaticStickerCatalog、虚拟列表、managed original/thumbnail、引用保护与显式清理、asset routes | WP-5 | +| 色卡与动态贴纸 | deterministic palette、dynamic provider registry | WP-4 / WP-5 | +| 导出 | browser compositor、latest export API | WP-4 | +| 运营后台 | Admin API/UI、模型配置集合、私有告知确认、贴纸清理、service state、audit | WP-6 | +| 素材发布与内部预览 | immutable release、grants、three access routes | WP-5 / WP-6 | +| 本机数据与资源访问 | Supervisor、浏览器 gate、辅助系统界面、SQLite、LocalDataRoot、storage state | WP-0 / WP-2 | +| 隐私、审计与免费服务边界 | deletion、retention、audit、quota state | WP-1 / WP-6 | + +### 14.4 AC 唯一主责映射 + +| 主责工作包 | 当前 AC | +| --- | --- | +| WP-0 基础工程与本机边界 | AC-24、AC-46、AC-55、AC-56 | +| WP-1 认证、身份、隐私与审计基础 | AC-01、AC-02、AC-22、AC-33、AC-39、AC-45、AC-49 | +| WP-2 项目、点数与 GenerationJob | AC-03、AC-04、AC-05、AC-06、AC-20、AC-21、AC-28、AC-29、AC-34、AC-35、AC-36 的 P0-A 子集、AC-38 的 P0-A 子集、AC-43、AC-44、AC-51、AC-53 | +| WP-3 三模型契约验证与适配 | AC-30、AC-40 | +| WP-4 桌面编辑器与导出 | AC-07、AC-09、AC-10、AC-11、AC-12、AC-14、AC-16、AC-17、AC-18、AC-19、AC-23 的 P0-A 子集、AC-27、AC-32 | +| WP-5 素材转换、发布与白名单 | AC-13、AC-15 的 P0-A 子集、AC-31、AC-42、AC-48 | +| WP-6 运营后台与外部服务控制 | AC-25、AC-47、AC-50、AC-52 | +| WP-7 P0-A 发布准备 | AC-41 | + +AC-08、AC-26、AC-37、AC-54 没有当前主责工作包。交叉 AC 可作为其他工作包的依赖或回归输入,但只能由上表主责工作包宣告完成。 + +### 14.5 PRD 需求 ID 覆盖索引 + +| 主责工作包 | PRD 需求 ID | +| --- | --- | +| WP-0 | PRIV-01、PRIV-02、NFR-01、NFR-07、NFR-09 | +| WP-1 | AUTH-01、AUTH-02、AUTH-03、AUTH-04、AUTH-05、AUTH-06、AUTH-07、PRIV-03、PRIV-04、PRIV-06、NFR-04 | +| WP-2 | CREDIT-01、CREDIT-02、CREDIT-03、CREDIT-04、CREDIT-05、CREDIT-06、PROJECT-01、PROJECT-02、PROJECT-03、PROJECT-04、PROJECT-05、PROJECT-06、PROJECT-07、PROJECT-08、PROJECT-09、GEN-03、GEN-04、GEN-05、GEN-06、GEN-11、GEN-12、GEN-14、GEN-16、NFR-05 | +| WP-3 | GEN-01、GEN-02、GEN-07、GEN-08、GEN-09、GEN-10、GEN-13、GEN-15、ADMIN-03 | +| WP-4 | EDITOR-01、EDITOR-02、EDITOR-03、EDITOR-04、EDITOR-05、EDITOR-06、EDITOR-07、EDITOR-08、EDITOR-09、TEXT-01、TEXT-02、TEXT-03、TEXT-04、TEXT-05、TEXT-06、TEXT-07、TEXT-08、TEXT-09、TEXT-10、TEXT-11、TEXT-12、TEXT-13、TEXT-14、COL-01、COL-02、COL-03、COL-04、COL-05、DYN-01、DYN-02、DYN-03、DYN-04、DYN-05、DYN-06、DYN-07、DYN-08、DYN-09、EXPORT-01、EXPORT-02、EXPORT-03、EXPORT-04、EXPORT-05、EXPORT-06、NFR-03、NFR-06 | +| WP-5 | STATIC-01、STATIC-02、STATIC-03、STATIC-04、ADMIN-06、ADMIN-07、PRIV-05、NFR-02 | +| WP-6 | ADMIN-01、ADMIN-02、ADMIN-04、ADMIN-05、ADMIN-08、ADMIN-09、NFR-08 | + +GEN-05、GEN-14、GEN-16、AUTH-07、CREDIT-06、ADMIN-06、ADMIN-07、NFR-02、NFR-04、NFR-07 和 PRIV-02 具有跨工作包依赖,但上表给出唯一主责。WP-7 不拥有新的产品需求 ID,只负责汇总全部当前需求和适用 AC 的发布证据。 + +### 14.6 跨工作包依赖矩阵 + +本表只定义必须交付的接口和回归关系,不改变 14.4/14.5 的唯一主责。 + +| 上游工作包/契约 | 下游工作包 | 需求/AC 交叉 | 强制交付边界 | +| --- | --- | --- | --- | +| WP-0 LocalBackendStorageState、纯 JSON/二进制路由分界 | WP-2 项目、删除与清理 | PROJECT-04-09、NFR-07/09、17.19;AC-20、35、36 P0-A、38 P0-A、43、55、56 | WP-2 只通过已提交资源 ID 保存 JSON;full 可保存,unavailable 按可写性拒绝;删除完成后回传重测 | +| WP-0 LocalBackendStorageState、容量预留 | WP-4 导出 | EXPORT-05/06、NFR-03/09;AC-23 P0-A、24、35、38 P0-A、55、56 | 客户端下载始终与 latest export 上传解耦;full/unavailable 不得覆盖旧 latest export | +| WP-0 LocalBackendStorageState、managed/derived 写策略 | WP-5 素材编译与发布 | STATIC-04、ADMIN-06/07、PRIV-05、NFR-02/07;AC-31、42、46、48、55 | 既有归档不复制;上传只进 managed;无法直接服务的最小产物去重后进 derived,两者均受 5 GB 约束 | +| WP-0 存储测量、日志和告警 | WP-6 运营后台 | ADMIN-08/09、17.16/17.19;AC-41、50、52、55 | 后台只读取非敏感容量/健康摘要,不能上调固定上限、强制恢复或回显绝对路径 | +| WP-0 安全配置修订/生效协议 | WP-1 认证、WP-6 审计 | AUTH-07、ADMIN-09、17.16;AC-45、49、50 | 只有 API 原子应用后才生效;离线执行者不成为审计角色;失败不部分生效 | +| WP-2 任务诊断字段 | WP-3 Adapter、WP-6 诊断 | GEN-13/16、ADMIN-04;AC-40、51、52、53 | 未知上游结果进入人工成本核对,不新增任务/错误状态,不补扣用户点数 | +| WP-0 浏览器支持记录、gate 与辅助状态 | WP-1 至 WP-7 全部 Web/API | NFR-01、17.1;AC-24、25、30、31、40、50、51、55 | 未通过品牌/major/平台检查只能读取最小引导;全部产品路由先执行 gate;辅助状态不泄露路径或正文 | +| WP-3 ModelConfig set 与 runtime availability | WP-2 生成、WP-6 模型/服务后台 | GEN-01/13-15、ADMIN-03、17.2/17.17;AC-30、40、51 | 默认/优先级以配置集合原子切换;推荐只从独立运行时状态派生;余额/契约降级不回写配置 | +| WP-1 UserProfile 告知字段 | WP-6 私有内容区域与访问审计 | ADMIN-05、17.1/17.10;AC-25、50 | 后端按身份+版本持久确认;区域服务端门禁;每次打开仍先写 PrivateContentAccessLog | +| WP-2 项目资源引用、WP-5 贴纸文件关系 | WP-6 清理后台、WP-0 容量重测 | STATIC-04、ADMIN-06/09、17.7/17.16/17.19;AC-31、50、55 | active/trashed/发布及其他有效引用阻止整批清理;排队不减容量;物理删除后重测才更新/恢复 | + +### 14.7 v1.10 重点 AC 工程闭环 + +| AC | 数据/状态 | API/事务 | 必须进入 tdd.md 的基础设施断言 | +| --- | --- | --- | --- | +| AC-24 | `browser_support_release`、Supervisor 辅助状态 | support check + 全产品路由前置 gate;426 无绕过 | 真实受支持 Chrome/Edge 通过;其他品牌、错误 major、UA-CH 缺失/冲突、直调 API、局域网/其他设备均阻断 | +| AC-25 | UserProfile 告知 version/time、PrivateContentAccessLog | ack 事务、区域服务端前置校验、打开前审计 | 跨标签/重启/重登/两浏览器同版本不重复;升版重新提示;每次内容读取独立审计 | +| AC-30 | immutable config set、唯一正整数 priority、runtime availability | 完整集合 CAS 保存、默认替换同事务、模型读取派生推荐 | 初始 seed;缺失/非正/重复;无替代停默认;合法原子替换;默认运行时不可用和全不可用 | +| AC-31 | managed original/thumbnail、release/project refs、cleanup intent | 上传/缩略图容量预留;候选、确认、引用复核、排队、物理删除/重测 | 两类字节计入;full/unavailable 阻断;有引用整批拒绝;无引用显式清理;旧项目继续渲染 | +| AC-40 | 三个独立 config/Adapter/contract evidence | 分模型验证与 config/runtime 分离 | 三模型各自端点、输入、参考图、比例、同步/异步、任务查询、九类错误和结算证据,任一变化重置 unverified | +| AC-50 | append-only AdminOperationLog / PrivateContentAccessLog | 模型配置、贴纸清理各阶段和服务恢复事务内审计;私有打开独立审计 | 请求、引用校验、拒绝/排队、物理结果完整且 180 天内不可改删;无敏感正文 | +| AC-51 | GatewayBalanceState + model_runtime_availability | model/account/unknown 运行时影响事务、人工恢复 | 三种影响均不改 enabled/is_default/priority;只释放一次点数;推荐重新派生;恢复审计 | +| AC-55 | LocalBackendStorageState、reservation、managed file kinds | 写前 `>`、等于允许后 full;清理后物理重测 | 精确阈值、等号、超限、原图/缩略图计量、full/unavailable 动作矩阵、清理前不减字节及恢复条件 | + +## 15. 实施阶段与依赖顺序 + +以下是工程阶段,不是 tasks.md。后续 tasks.md 必须在每个工作包内部继续拆分,但不得改变顺序、依赖和完成条件。 + +### WP-0 基础工程与本机边界 + +- **需求/AC**:NFR-01、NFR-07、NFR-09、PRIV-01/02、第 17.19、18.9/18.10、23.4;AC-24、46、55、56。 +- **输入**:冻结文档、固定技术栈、Gitea、当前 Windows 电脑。 +- **产出**:monorepo、冻结锁文件、版本兼容性预检证据、OpenAPI 基础、SQLite 迁移、LocalDataRoot、storage state、.NET 托盘、API/Worker 生命周期、浏览器 support gate、辅助系统界面状态/诊断契约、日志、隔离 CI、便携 ZIP、`START-HERE.txt` 和 SHA-256。 +- **依赖**:无。 +- **完成条件**:冻结安装、TypeScript、React/Fabric、Fastify、better-sqlite3 native 和便携包预检全部通过;单实例启动/退出、固定端口、回环绑定、浏览器品牌/major/平台硬门禁、无 UA-CH 阻断、API/Worker degraded、脱敏诊断、Gitea 隔离 CI、容量 `>`/`=` 状态和只读资源 ID 骨架通过集成测试。 +- **不能提前宣告**:任一固定依赖未通过预检且未正式修订 DevelopmentPlan,或未完成不可写/full 恢复、日志轮转、无备份/不迁移、局域网拒绝、产品 API 门禁和辅助界面敏感字段扫描时,基础环境不完成;AC-24 最终通过仍须等待 WP-7 写入真实稳定浏览器记录。 + +### WP-1 认证、权限、数据模型与审计基础 + +- **需求/AC**:AUTH-01-07、PRIV-03/04/06、ADMIN-09、第 17.1、17.10、17.11、17.16;AC-01、02、22、33、39、45、49;另向 WP-6 提供 AC-25 的 UserProfile 数据依赖,不取得其主责。 +- **输入**:WP-0;Resend mock;Windows 凭据和离线白名单命令。 +- **产出**:邀请、验证码挑战、普通/管理员分离会话、10 人名额、多 super_admin、安全配置修订/原子生效协议、开发账号脚本、UserProfile 私有内容告知字段/迁移、注销、匿名保留、两类审计基础。 +- **依赖**:WP-0。真实认证验收依赖 Resend 发布门槛。 +- **完成条件**:模拟状态机、原子邀请码核销、30 天撤销、全管理员停用/离线恢复、安全配置成功/失败生效审计、告知版本字段跨重启持久化、凭据缺失/清除/重启和全传递链零泄漏、注销不可恢复和 180 天清理通过。 +- **不能提前宣告**:开发预置账号或模拟邮件不能宣告 Resend、正式注册、管理员认证或 AC-41 完成。 + +### WP-2 项目、点数与 GenerationJob + +- **需求/AC**:CREDIT-01-06、GEN-03-06/11/12/14-16、PROJECT-01-09、第 17.3-5/9/18;AC-03-06、20、21、28、29、34、35、36 P0-A、38 P0-A、43、44、51、53。 +- **输入**:WP-0、WP-1 数据与会话;模拟 Adapter。 +- **产出**:项目/历史、failed_empty、自动保存、冲突、回收站、`project_asset_refs`、点数账户与流水、持久队列、单任务索引、模型运行时可用性消费、文件生命周期和九类错误路径。 +- **依赖**:WP-0、WP-1。真实完成态联调依赖 WP-3。 +- **完成条件**:并发提交、幂等、崩溃恢复、只结算/释放一次、上游不确定成本核对、active/trashed 项目资源引用一致、full 纯 JSON 保存/二进制阻断、同机多标签和本地导出例外均通过 SQLite/API/Worker 测试。 +- **不能提前宣告**:模拟成功不能宣告三模型真实结算或 AC-40 完成。 + +### WP-3 三模型契约验证与适配 + +- **需求/AC**:GEN-01/02/05/07-10/13-16、ADMIN-03、GatewayBalanceState;AC-30、40。 +- **输入**:WP-2 Adapter 接口;平台文档/支持确认;受控凭据与既有额度。 +- **产出**:初始三模型 seed、immutable config set/迁移、唯一优先级与默认替换事务、读取 API 的配置默认/运行时状态/recommended_model_id、三个独立 Adapter、三套脱敏证据、route/error mapping profile、比例/参考图矩阵、同步/异步恢复、余额影响与真实点数结算联调。 +- **依赖**:WP-2;AI 网关外部阻塞解除。 +- **完成条件**:初始化默认/1-2-3 优先级、非法优先级整批拒绝、默认模型合法原子替换、runtime 不回写配置、空推荐、三个模型在最后一次契约配置变化后均 verified,以及 AC-30/40 全矩阵通过。 +- **不能提前宣告**:配置集合能产生零/多个 enabled default、优先级可并列/非正、运行时降级改写配置,或任何一个模型未验证/只验证文字/单比例/无参考图/无错误时,WP-3 和真实结算联调不完成。 + +### WP-4 桌面编辑器与导出 + +- **需求/AC**:EDITOR-01-09、TEXT-01-14、COL-01-05、DYN-01-09、EXPORT-01-06;AC-07、09-12、14、16-19、23 P0-A、27、32。 +- **输入**:WP-2 Project/Canvas API;WP-5 的稳定资源 fixture 可并行替换。 +- **产出**:Fabric 编辑器、底图、选择/变换/撤销、文字/字体、色卡、动态 provider、客户端 JPG/PNG、latest export 和性能 harness。 +- **依赖**:WP-2。最终视觉依赖 WP-5 白名单资源。 +- **完成条件**:50 元素预算、Chrome/Edge 核心白名单一致性、保存失败本地导出和四种比例导出通过。 +- **不能提前宣告**:占位字体、未校验模板、只在一个浏览器通过或后端失败时不能下载,均不完成。 + +### WP-5 素材转换、发布与 P0-A 白名单 + +- **需求/AC**:STATIC-01-04、ADMIN-06/07、PRIV-05、NFR-02/07、第 17.6-8/12-14、第 18 节;AC-13、15 P0-A、31、42、48。 +- **输入**:只读规范归档和交接 manifest;WP-0 资源路由;WP-4 renderer contract。 +- **产出**:1,407 项 StaticStickerCatalog、全部复杂模板注册、P0-A 白名单 release、immutable manifest、哈希、转换产物、后台原图/缩略图关系、显式历史清理 API/队列/审计、三类路由和 150 MB LRU。 +- **依赖**:WP-0;可与 WP-4 前半并行,最终集成依赖 WP-4。 +- **完成条件**:源目录零改写、无全库/等价派生副本、managed/derived 分类和内容去重、原图/缩略图计量、full/unavailable 阻断、清理候选/确认/引用竞争/物理删除/重测、计数/哈希、公开 manifest 隔离、授权撤销立即生效和旧版本项目渲染均通过。 +- **不能提前宣告**:贴纸历史文件仍有发布或 active/trashed 项目引用、只排队未物理删除、容量提前扣减或存在自动清理时不完成;色卡/动态贴纸不能计入 25 part;白名单外 passed 不能逐批向普通用户 enabled;PRIV-05 不得被扩张为 P0-A/P0-B 商业授权台账阻塞。 + +### WP-6 运营后台与内部运营控制 + +- **需求/AC**:ADMIN-01-09、AUTH-07、CREDIT-06、GEN-15、ExternalServiceUsage、GatewayBalanceState;AC-25、47、50、52。 +- **输入**:WP-1/2/3/5 的服务和审计接口;Resend/Amap mock。 +- **产出**:用户、邀请、点数、模型配置/运行时状态、生成、素材/历史清理、预览、服务、存储、指标、审计后台;私有内容告知 ack/区域服务端门禁/逐次访问审计;人工恢复流程。 +- **依赖**:WP-1、WP-2、WP-5;模型页最终状态依赖 WP-3。 +- **完成条件**:告知确认按身份+版本跨标签/重启/重登持久且升版重置;每次私有内容打开仍有独立日志;模型/贴纸清理等全部敏感操作有不可变审计;硬上限不可上调;恢复前置条件强制执行。 +- **不能提前宣告**:以浏览器/session 状态代替告知持久化、确认后省略逐次访问审计、贴纸清理缺少请求/复核/结果日志、后台按钮缺服务端授权/事务,或可回显路径/密钥/正文时不完成。 + +### WP-7 P0-A 验收准备与发布 + +- **需求/AC**:第 22 节全部 P0-A 必验及四个 P0-A 子集、第 23 节;AC-41 主责。 +- **输入**:WP-0 至 WP-6 完成;真实外部依赖;当前稳定版 Chrome/Edge;规范素材根。 +- **产出**:发布 ZIP、SHA-256、带 Windows/Chrome/Edge 支持记录的 RELEASE.json、`START-HERE.txt`、辅助系统界面验收、验收环境、AC 结果、视觉/性能基准、外部门槛记录,以及页面/设置区持续显示的固定文案“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” +- **依赖**:所有当前工作包和三个外部阻塞全部解除。 +- **完成条件**:适用 AC 全部通过;真实稳定 Chrome/Edge full version 已验收并固化 major,其他浏览器/错误 major/无法识别/直调 API 无绕过;两个输入文档哈希未变化;`START-HERE.txt` 的未签名、SmartScreen/误报和 SHA-256 核验流程可用;固定数据风险文案持续可见;没有 P0-B 能力混入。 +- **不能提前宣告**:任一模型 unverified、模型配置/运行时状态仍混写、Resend 送达不达标、高德未核验、贴纸清理/告知确认/storage/素材/浏览器或辅助诊断门槛失败时不得开始或完成 P0-A 实际验收。 + +## 16. P0-B 未来开放门槛 + +P0-B 不进入当前工程实现。未来单独立项时,必须重新定版公司系统合并、正式存储、远程部署、域名、HTTPS、数据隔离、桌面继承和手机横竖屏完整编辑,并执行 AC-08、AC-26、AC-37、AC-54 及通用 AC 的 P0-B 子集。 + +扩大普通账号上限、开放全部复杂模板和移动端能力是同一发布门槛的一部分,不得把 P0-B 解释为只扩大人数。白名单外 full_p0 模板可以提前内部导入和 passed,但只有全部剩余复杂模板 passed,且所有 P0-B 门槛同时满足后,才可通过一次事务化发布全部 enabled。P0-A 数据不自动或手动迁移到该未来系统。 + +未来一次性开放前必须同时具备:P0-A 全部适用 AC 通过;332 个文字模板、86 个字体项、1,407 张普通贴纸、16 个色卡和 35 个动态贴纸完成全量验收;AC-26 和 AC-37 通过;白名单外复杂模板全部 passed;普通账号上限配置并验证为 50;公开 manifest 不含 internal_preview_asset;三个模型在最后一次契约配置变化后均 verified;公司系统合并、正式存储、远程部署、域名、HTTPS、数据隔离以及桌面与手机横竖屏能力全部定版并验收。缺少任一条件时仍保持 P0-A 的 10 人上限和普通用户白名单。 + +## 17. 后续文档接口 + +### 17.1 UIDesign.md 输入 + +UIDesign.md 接收页面/入口边界、角色权限、状态枚举、错误 `message_key`、保存/冲突/容量/服务暂停行为、P0-A 设备边界、可访问性要求和资源缓存限制;还必须接收 3.6 的无绕过浏览器阻断页、模型 configured default/runtime availability/recommended 三层显示、私有内容告知版本确认、贴纸历史清理确认/引用冲突/排队/物理结果,以及 3.7 的 Windows 托盘、启动失败、端口占用、API/Worker degraded、诊断入口和脱敏诊断字段。本地测试页或设置区必须持续显示且不得改写固定文案:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。”UIDesign.md 决定其布局和可访问呈现,但不能将它降级为仅首次启动、仅发布说明或可关闭后永久消失的提示。其他布局、视觉、图标、焦点顺序和具体中文文案由 UIDesign.md 决定,但不能改变 API 状态机、错误动作、AC、辅助状态允许动作或阶段范围。 + +### 17.2 tdd.md 输入 + +tdd.md 接收 OpenAPI 分组、稳定错误码、SQLite 不变量、事务/幂等规则、ModelConfig 集合/运行时推荐、Adapter 契约矩阵、九类错误、文件一致性、三类资源、贴纸显式清理、私有告知确认、工作包 AC 主责和性能/视觉阈值。它还必须接收凭据缺失/清除/重启与泄漏扫描门槛、UA-CH 硬门禁和真实稳定浏览器要求、辅助系统界面脱敏断言、Service Worker/Cache Storage/IndexedDB 持久存储断言、容量 `>`/`=` 与 full 纯 JSON/二进制拒绝矩阵,以及上游成本核对路径。tdd.md 补充 `RELEASE.json` 对应的具体 Windows build、浏览器 full version、DPR、视口、fixture、逐场景步骤和断言;必须把 `reference_invalid` 与 `safety_rejected` 分开。 + +### 17.3 tasks.md 输入 + +tasks.md 接收 WP-0 至 WP-7 的顺序、依赖、14.6 跨包矩阵、14.7 重点 AC 闭环、组件边界、产出、完成条件和禁止提前宣告事项,再拆为可执行任务。不得创建 P0-B 当前任务,不得重新选择技术栈,也不得把外部阻塞伪装成已完成开发任务。原型或兼容性验证失败时,必须先正式修订 DevelopmentPlan;实现任务不能把固定端口、进程模型、浏览器门禁、模型配置集合、SQLite 保护、容量等号语义、贴纸清理、日志轮转、凭据传递或依赖版本改成可选实现。 + +## 18. 风险与工程控制 + +| 风险 | 控制 | +| --- | --- | +| 网关公开说明与真实图片协议不一致 | 三模型独立证据;未验证即运行时不可用于新任务但不改配置;不从模型名称推断 | +| SQLite API/Worker 并发写 | WAL、busy timeout、BEGIN IMMEDIATE、部分唯一索引、事务重试仅限尚未产生外部副作用的操作 | +| Worker 崩溃造成重复上游调用 | 租约恢复只 poll,不重新 submit;无法查询则失败释放,用户手动重试;上游可能消耗进人工成本核对 | +| 文件与数据库跨事务不一致 | staging、哈希、同卷原子改名、managed_files、outbox 和启动对账 | +| 本机磁盘不足 | 精确 `>` 容量预检、等号提交后 full、storage reservation、full/unavailable 硬停止、物理删除后重测、无自动删除 | +| 默认模型、优先级和运行时状态相互覆盖 | immutable config set 原子切换、数据库集合约束、独立 runtime availability、读取时派生推荐 | +| 贴纸清理与项目/发布并发产生悬空引用 | 候选快照、BEGIN IMMEDIATE 复核、active/trashed/发布引用表、整批拒绝、队列幂等和物理后重测 | +| 私有内容告知被浏览器状态绕过 | UserProfile 身份+版本持久确认、区域服务端门禁、升版失效、每次打开独立访问审计 | +| 不支持浏览器绕过前端检查 | 最小 gate shell、UA-CH 交叉校验、发布 major 记录、全部产品路由前置中间件、426 无绕过 | +| 浏览器私有草稿泄漏 | 私有编辑态仅内存;Cache/IndexedDB 仅公开资源;自动化扫描浏览器存储 | +| 素材复制或源目录污染 | 只读根、manifest allowlist、release job 独立输出、源目录前后哈希/时间检查 | +| 字体与导出漂移 | 固定字体资源、FontFace ready、Canvas Schema、Chrome/Edge视觉阈值和人工复核 | +| 托盘或子进程残留 | 单实例 mutex、命名管道、健康检查、有限重启、15 秒优雅退出 | +| 未签名程序被 Windows 警告或杀软误报 | `START-HERE.txt`、SHA-256、固定 Gitea 构建记录和明确的项目方本机核对流程;不声称已签名,不指导关闭安全防护 | +| P0-A 被误写成远程正式系统 | 固定回环地址、无远程组件、AC-24 网络检查、P0-B 仅保留门槛 | + +## 19. 完成定义 + +DevelopmentPlan 的工程方案落实后,P0-A 只有在以下条件同时满足时可标记完成: + +- WP-0 至 WP-7 全部达到各自完成条件; +- FeatureSummary 十三个模块、PRD 十九个数据契约和九类 GenerationErrorCategory 均有实现与测试落点; +- AC-01-07、09-14、16-22、24-25、27-35、39-53、55-56 通过;AC-15、23、36、38 仅 P0-A 子集通过; +- AC-08、26、37、54 没有被实现结果冒充为当前完成条件; +- AI 网关、Resend、高德和本机发布门槛全部解除; +- 普通账号系统上限为 10,`super_admin` 不占普通账号名额;P0-A 人工验收固定使用 1 个 `super_admin` 和 1 个普通账号,跨普通用户隔离仅通过正常认证链临时创建第二普通账号,验收后注销并按规则清理; +- LocalDataRoot 与素材根严格分离,5 GB/80%/90%、150 MB LRU、三类资源隔离和无私有草稿持久化通过; +- 后台上传普通贴纸原图和缩略图均计入 managed_content_bytes;预计写入后超过上限拒绝、恰好等于上限允许后进入 full;历史文件只经 super_admin 显式确认、无有效引用、不可变审计、物理删除和重测后释放容量; +- 初始三模型默认和 recommendation_priority 为 1/2/3;后台配置始终恰好一个 enabled default,运行时不可用不改写 enabled/is_default/priority,recommended_model_id 只按可用模型派生且允许为空; +- 私有内容告知按 super_admin 身份与当前版本后端持久确认,升版后重确认;任何图片或完整提示词读取仍先写独立 PrivateContentAccessLog; +- 发布包只服务当前 Windows 电脑上 RELEASE.json 记录 major 的 Chrome/Edge;其他品牌、不支持版本或无法识别的浏览器只有无绕过引导,局域网、手机和其他设备无法访问; +- Windows 托盘、启动失败、固定端口占用、API/Worker degraded 和诊断结果具有稳定脱敏界面契约,不能泄露密钥、绝对路径或私有正文; +- 发布产物、日志、数据库配置、manifest 和文档中不存在真实密钥、验证码、会话令牌、管理员邮箱或硬编码用户绝对路径; +- 本地测试页或设置区始终明确显示“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。”;用户只能主动下载原始生成图和 JPG/PNG 成品。 +- 所有固定技术决策已通过兼容性/原型门槛;任一失败只能先正式修订 DevelopmentPlan,不得由实现人员自由替换。 diff --git a/FeatureSummary.md b/FeatureSummary.md new file mode 100644 index 0000000..4592b23 --- /dev/null +++ b/FeatureSummary.md @@ -0,0 +1,260 @@ +# Dada Feature Summary 1.1 版本 + +## 1. 文档定位 + +| 项目 | 说明 | +| --- | --- | +| 唯一需求来源 | `PRD.md` v1.10,状态为“P0-A 本机测试需求冻结稿” | +| 当前交付 | 仅 P0-A | +| 文档用途 | 为后续 `DevelopmentPlan.md` 提供范围导航、需求索引、状态与契约入口;不替代 PRD,不增加或重新解释需求 | +| 冲突处理 | 本文与 PRD 不一致时以 PRD 为准;产品行为变化必须先升版 PRD | +| 非本文件职责 | 不定义技术选型、数据库/API 结构、UI 细节、测试步骤或任务拆分 | + +P0-A 是只在项目方当前 Windows 电脑运行的单机测试版:本地网页连接本地后端,本地后端只监听 localhost,并把业务数据保存到本机 `LocalDataRoot`。正式支持环境仅为该电脑上的当前稳定版 Chrome 和 Edge;其他浏览器及不受支持的 Chrome/Edge 版本只能进入无绕过入口的“浏览器不受支持”引导页,不能进入任何产品功能。 + +页面或设置区域必须持续显示: + +> 测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。 + +## 2. 产品目标与核心闭环 + +Dada 面向受邀的图片创作者。普通用户通过邀请码和邮箱验证码注册,填写创作署名与社交 ID,使用三个 AI 模型之一生成底图,在电脑画布中添加和编辑文字模板、普通贴纸、色卡及动态贴纸,由本地后端自动保存项目,最后下载 JPG/PNG 成品;用户也可在同一电脑再次打开项目、查看历史并下载原始生成图或已保存的最新成品。 + +核心闭环为: + +`邀请注册或邮箱登录 -> 查看点数与模型限制 -> AI 生图 -> 电脑画布编辑 -> 本地后端自动保存 -> 下载 JPG/PNG -> 同机继续处理或再次下载` + +P0-A 的目标是验证上述完整闭环、三模型契约、点数原子结算、素材还原、本机数据边界、管理员运营和审计能力,不以远程部署或扩大参与人数代替产品验收。 + +## 3. 阶段边界 + +| 范围 | 定位 | 关键边界 | +| --- | --- | --- | +| P0-A,当前交付 | 当前 Windows 电脑上的 localhost 单机测试版 | 本地网页、本地后端、本机磁盘;最多 10 个普通账号;电脑完整编辑;核心复杂模板白名单;全部 1,407 张初始规范普通贴纸;只允许本机当前稳定版 Chrome/Edge 进入产品,其他浏览器和不支持版本硬阻断 | +| P0-B,未来阶段 | 公司系统合并后的完整 P0 | 最多 50 个普通账号;全部规定素材一次性公开;正式存储与远程部署;正式数据隔离;手机完整编辑;必须满足第 10 节全部门槛后另行开放 | +| P1,后续候选 | 不属于 P0-A | 上传本地底图、空白纯色/渐变画布、为既有普通贴纸补标题和关键词后开放搜索、公开注册准备、高德公司主体迁移评估、公开合规与完整条款 | +| 明确不做 | 当前 PRD 已排除 | 付费订阅和在线支付、小红书账号绑定或直接发布、多页图组和批量生成、社区、脱离模板的普通文字、用户上传自定义贴纸、AI 生成贴纸、GIF/视频导出、复制生成、完整封面模板、提示词优化、局部重绘、去背景、专业调色、协作、项目版本历史、分组及图层面板/隐藏/锁定 | + +P0-A 不提供手机、局域网、远程访问或跨设备流程,不使用 COS、公司对象存储、公司服务器、备案域名、HTTPS 站点或其他远程文件存储。P0-A 后端仍需从本机向 AI 网关、Resend 和高德发起必要的外网 HTTPS 请求。 + +## 4. 用户与权限边界 + +| 身份或主体 | 能力边界 | +| --- | --- | +| 访客 | 通过浏览器支持检查后可访问独立注册和登录入口;未登录时只能读取 `public_release_asset` 的公开只读资源,不能访问项目、私有图片、内部预览或后台;未通过检查时只能看到无继续入口的阻断引导页 | +| 普通用户 | 通过有效邀请码和 Resend 邮箱验证码注册,或以已注册邮箱验证码登录;管理自己的资料、点数、项目、生成历史、私有资源和导出;不能访问其他用户内容或后台 | +| 获内部预览授权的普通用户 | 仍是普通用户,只能额外查看 `AssetPreviewGrant` 授权批次中 `internal_preview` 或 `passed` 的复杂模板;不获得后台、调点、模型配置、邀请码或私有内容权限 | +| `super_admin` | P0 唯一后台角色;管理邀请码、账号状态、点数、模型配置、普通贴纸、素材版本、内部预览、生成记录、辅助服务和审计;确认私有内容告知后按规定打开私有内容并产生独立访问审计;可确认并显式清理符合条件的无引用历史上传贴纸文件 | +| 部署人员 | 维护当前阶段后端安全配置;不是 Dada 用户或后台角色,没有产品账号、登录入口、后台会话或产品权限;配置生效由 `system` 审计 | + +P0 允许存在多个权限相同的 `super_admin`,第二个白名单邮箱可创建独立备用管理员。P0-A 人工验收固定使用项目方控制的 1 个 `super_admin` 和 1 个普通账号;跨普通用户隔离由测试程序在同机独立浏览器配置中临时创建第二个普通账号验证并清理。`super_admin` 不占普通账号名额。 + +私有内容区域的全局提示确认按“`super_admin` 身份 + 告知版本”持久记录,不按浏览器、标签页或会话记录;同一身份确认同一版本后不重复显示,告知文案升版后重新提示。该确认不替代逐次审计,每次打开图片或完整提示词仍写入独立 `PrivateContentAccessLog`。 + +账号状态只有 `active`、`suspended`、`deleted`。`active` 和 `suspended` 普通账号占用名额,`deleted` 释放名额;注销后旧数据不可恢复,原邮箱只能用新邀请码创建全新账号。 + +## 5. P0-A 功能摘要与追踪矩阵 + +下表只承担 PRD 与 AC 导航作用。AC 索引已按第 22 节分类表过滤,不把未来 P0-B 场景纳入当前实现。 + +| 模块 | P0-A 摘要 | PRD 索引 | 当前 AC 索引 | +| --- | --- | --- | --- | +| 邀请注册与邮箱登录 | 独立注册/登录入口;邀请码在发码前校验;Resend 验证码;30 天会话;创作署名、社交 ID 和版本化内测告知;管理员邮箱白名单与独立后台认证。开发环境可由初始化脚本预置一个普通测试账号,但正式认证验收必须真实调用 Resend,预置账号不能作为验收证据 | AUTH-01 至 AUTH-07 | AC-01、02、22、33、41、45、47、49 | +| 点数与原子结算 | 新用户默认 10 点;模型默认每次 1 点且可配置;维护 `available_balance` 与 `reserved_balance`;提交时原子冻结,成功只提交一次,失败/拒绝只释放一次;只有带原因和流水的管理员调点可使可用余额为负 | CREDIT-01 至 CREDIT-06 | AC-03 至 05、28、29、40、51、53、55 | +| 三模型 AI 生图 | 支持 `gemini-3.1-flash-image-preview`、`gemini-3-pro-image-preview`、`gpt-image-2`;输入仅含提示词、模型、比例、可选参考图和生成按钮;比例为 3:4、1:1、4:3、9:16;每次一张图。同账号同时只能有一个 `queued` 或 `running` 任务,再次提交返回当前任务,不创建第二个任务或重复冻结点数。后台始终恰好有一个已启用默认模型,停用当前默认模型必须原子指定合法替代默认模型。每个模型具有唯一正整数 `recommendation_priority`;运行时只在当前可用于新任务的模型中按数值由小到大返回推荐模型,无可用模型时返回空推荐,运行时降级不得回写启停、默认或推荐优先级。三个模型必须分别完成契约验证,不能按名称推断相同协议 | GEN-01 至 GEN-16、ADMIN-03 | AC-03 至 06、28、30、34、40、41、44、51、53、55 | +| 项目、历史、保存与冲突 | 新建创作建立项目,项目内继续生成只追加历史;项目比例创建后固定;最多 20 个 active 项目、每项目 10 张成功图。默认名来自提示词摘要和创建日期,用户可重命名并触发自动保存。无成功图的 `failed_empty` 草稿可批量移入回收站,含成功图的项目不得混入该快捷操作。删除后保留 720 小时;P0-A 只处理同机多标签 `state_version` 冲突 | PROJECT-01 至 PROJECT-09、GEN-11、GEN-12 | AC-03 至 07、20、21、34、35、AC-36 的 P0-A 子集、AC-38 的 P0-A 子集、43、44、55、56 | +| 电脑画布与底图编辑 | 任何产品功能前先执行浏览器支持检查;仅本机当前稳定版 Chrome/Edge 可进入完整编辑器,其他浏览器和不支持版本硬阻断且无继续入口。编辑器支持选择、移动、缩放、旋转、翻转、透明度、复制、层级、对齐吸附、循环选择、长按候选、多选和框选;无图层面板。覆盖元素最多 50 个。底图支持裁剪、适配、亮度、对比度、饱和度、色温和固定滤镜;换底图保留覆盖元素并重置底图编辑状态 | EDITOR-01 至 EDITOR-09、GEN-12、NFR-01 | AC-07、14、20、24、27、32、35、AC-36 的 P0-A 子集 | +| 文字模板与字体 | 添加文字必须先选模板;P0-A 仅公开 32 个白名单模板及其实际引用字体,并包含 FONT081。模板按分类和 catalog 稳定顺序展示,支持显示名称搜索和账号级最近使用;切换模板保留文本、换行和变换;支持内容、填充、描边、背景、对齐、行距和字距等冻结能力。普通贴纸关键词搜索仍属于 P1 | TEXT-01 至 TEXT-14 | AC-09 至 12、19、32 | +| 25 个普通贴纸 part | 初始规范素材为 part1 至 part25 的 1,407 张 PNG,色卡和动态贴纸不计入这些 part。支持虚拟列表、懒加载和账号级最近使用。`super_admin` 可上传 PNG/WebP,配置稳定 ID、既有 part、顺序、启停和可选内部名称;上传原图及 Dada 生成的缩略图写入 `LocalDataRoot` 并计入 `managed_content_bytes`,只读规范素材库不计入。`full/unavailable` 阻断上传、缩略图生成和其他受管二进制写入。无引用历史上传文件只能由 `super_admin` 确认并审计后显式清理;下架只阻止新添加,已有项目按保存版本继续渲染 | STATIC-01 至 STATIC-04、ADMIN-06、第 17.7、17.19 节 | AC-13、14、27、31、32、41、46、48、50、55 | +| 色卡与动态贴纸 | P0-A 色卡仅启用 COLOR001、COLOR002、COLOR008、COLOR016;从当前底图未经裁剪、调色和滤镜处理的原始图片中在浏览器本地稳定提取五色,裁剪、调色或滤镜变化不重新提取。动态贴纸仅启用 DYN001-004、007、008、011、012、015、016,编辑时注入时间、地点或身份数据,导出为静态内容。DYN004 在调用定位前先告知原始坐标会保存到项目并显示在成品中,用户确认后才调用定位;DYN012 使用 FONT081 并标明替代字体 | COL-01 至 COL-05、DYN-01 至 DYN-09 | AC-07、AC-15 的 P0-A 子集、AC-16 至 19、32、41、47 | +| 导出 | 客户端按画布真实像素合成 JPG/PNG,无 Dada 水印;未提交文字或样式修改需确认,确认后形成一次撤销记录并导出,取消则不提交也不导出。下载文件与写入 `LocalDataRoot` 的成品必须来自同一次合成;每种格式只保留最新一份。后端持久化失败时仍可下载,但不得更新 `latest_exports` | EXPORT-01 至 EXPORT-06 | AC-23 的 P0-A 子集、24、27、35、AC-38 的 P0-A 子集、55、56 | +| 运营后台 | 邀请码、账号状态、点数、模型默认与推荐配置、普通贴纸及无引用历史文件显式清理、素材版本、内部预览批次与授权、生成记录、辅助服务、操作审计和私有内容访问;私有内容列表默认隐藏正文,区域提示按管理员身份与告知版本持久确认,文案升版后重新提示,每次打开仍记录独立审计。P0 只有一种后台角色,但可有多个管理员账号 | AUTH-07、CREDIT-06、ADMIN-01 至 ADMIN-09 | AC-25、29 至 31、41、42、45、47、49 至 52、55 | +| 素材发布与内部预览 | 复杂模板按稳定 ID、版本和发布状态注册;P0-A 普通用户只见已通过且已启用的白名单。授权测试账号可见授权批次中的 `internal_preview` 或 `passed`;剩余 `full_p0` 模板可分批验收,但不能逐批向普通用户公开。公开、内部预览、用户私有三类资源严格隔离 | STATIC-04、ADMIN-06、ADMIN-07、NFR-07、第 17.6、17.11 至 17.14 节、第 18 节 | AC-31、32、41、42、46、48 | +| 本机数据与资源访问 | `LocalDataRoot` 与只读素材根目录分离;业务数据、后台上传普通贴纸原图和缩略图由本地后端写入本机磁盘,后两类计入 `managed_content_bytes`,只读规范素材库不计入。浏览器只用资源 ID 访问,不得取得目录绝对路径。`LocalBackendStorageState` 执行 5 GB 内容上限及 80%/90% 预警;`full/unavailable` 阻断新增受管二进制写入,历史上传贴纸文件不得自动清理,物理清理并重新计量后才更新容量状态。数据不自动备份、不迁移、不提供可编辑项目归档 | PROJECT-04 至 PROJECT-09、STATIC-04、ADMIN-06、NFR-07、NFR-09、第 17.19、18.9、18.10、23.4 节 | AC-20、24、31、35、AC-36 的 P0-A 子集、41、46、48、50、55、56 | +| 隐私、审计与免费服务边界 | API 密钥只在本地后端安全配置;用户内容私有;私有内容区域提示按 `super_admin` 身份与告知版本持久确认,告知升版后重新提示且每次打开继续写访问审计;注销立即删除业务内容,匿名事件和审计最多保留 180 天;Resend 与高德达到应用硬上限或供应商不可用时硬停止,不启用付费备用、自动购买或自动扩容;AI 网关不开自动充值 | PRIV-01 至 PRIV-06、NFR-05、NFR-08、ADMIN-05、ADMIN-08、ADMIN-09、第 23.1 至 23.5 节 | AC-22、25、39 至 41、47、49 至 53、56 | + +v1.10 重点变更的 AC 导航为:AC-24(不支持浏览器硬阻断)、AC-25(私有内容告知持久化与逐次审计)、AC-30(默认模型原子替换与运行时推荐)、AC-31(贴纸容量、写入阻断与显式清理)、AC-50(贴纸清理审计)、AC-51(运行时不可用不回写模型配置)和 AC-55(本机容量计量与恢复)。 + +### 5.1 P0-A 复杂素材白名单 + +| 类型 | 当前普通用户可见范围 | +| --- | --- | +| 文字模板 | FLOWER001-008、H001-008、TAG001-007、TAG051、SIMPLE001-008,共 32 个 | +| 字体 | P0-A 仅显示白名单模板实际引用的字体面板项,并强制包含 FONT081;不显示白名单之外的其他字体项 | +| 色卡 | COLOR001、COLOR002、COLOR008、COLOR016,共 4 个 | +| 动态贴纸 | DYN001、DYN002、DYN003、DYN004、DYN007、DYN008、DYN011、DYN012、DYN015、DYN016,共 10 个 | +| 普通贴纸 | part1 至 part25 的 1,407 张初始规范 PNG 全量可见;P0-A 不支持普通贴纸关键词搜索,该能力属于 P1 | + +白名单外复杂模板必须先注册,但对未授权普通用户不可发现;获授权内部测试账号仍可查看其授权批次中的 `internal_preview` 或 `passed` 模板。`full_p0` 模板可以分批进入 `passed`,只能在 P0-B 全部门槛满足后一次性改为 `enabled`。 + +## 6. 关键状态与规则 + +| 对象 | 状态与导航规则 | +| --- | --- | +| 浏览器支持检查 | 本机当前稳定版 Chrome/Edge 通过检查后才可进入产品;其他浏览器或不受支持版本只进入“浏览器不受支持”引导页,不提供继续或绕过入口 | +| 账号 | `active`、`suspended`、`deleted`;暂停账号不能注册或登录,注销立即失效且旧数据不可恢复 | +| `UserProfile` 私有内容告知确认 | 确认按 `super_admin` 身份与告知版本持久记录;同一版本不因新标签页、重启或重新登录重复提示,告知升版后重新提示;每次打开私有内容仍独立写入 `PrivateContentAccessLog` | +| `ModelConfig` 默认与推荐 | 所有配置中始终恰好有一个已启用默认模型;停用当前默认模型与指定合法替代模型必须原子成功或整体失败。`recommendation_priority` 为不并列的唯一正整数,1 最高;缺失、非正整数或重复时整次配置保存失败且不部分写入。运行时推荐只从当前可用于新任务的模型派生,无可用模型时为空,不形成第二个默认状态或回写配置 | +| `GenerationJob` | `queued`、`running`、`succeeded`、`failed`、`rejected`;同账号已有 `queued` 或 `running` 时不创建第二个任务;成功只提交一次冻结点,失败或拒绝只释放一次 | +| `Project` | `active`、`trashed`、`purged`;只有 active 计入 20 个上限,trashed 在 `deleted_at + 720 小时` 前可在名额允许时恢复,purged 用户侧不可恢复 | +| `save_status` | `saved`、`dirty`、`saving`、`failed`、`conflicted`;刷新、关闭或崩溃后只恢复最后一次本地后端 `saved` 状态,浏览器不保存可恢复的私有草稿 | +| `TemplateRegistry.release_status` | `imported`、`internal_preview`、`passed`、`enabled`、`disabled`;授权测试账号可见授权批次中的 `internal_preview` 或 `passed`,`full_p0` 不得据此逐批公开 | +| `ExternalServiceUsage.service_status` | `active`、`paused_quota`、`paused_provider`、`disabled`;发布前送达测试失败只阻止发布,不创建运行时质量状态。只有免费周期或供应商可用条件重新满足后,`super_admin` 才能恢复暂停服务并留下后台审计 | +| `GatewayBalanceState` | 余额为可用、不足或无法可靠确认;影响范围为模型、账户或未知。模型级只把对应模型标记为运行时不可用,账户级或未知处理共享结算账户的全部模型;不得改写 `ModelConfig.enabled`、`is_default` 或 `recommendation_priority`,确认真实余额恢复后才可由 `super_admin` 恢复运行时可用状态并审计 | +| `LocalBackendStorageState` | `capacity_notice_level` 为 `normal`、`warning`、`critical`;`storage_status` 为 `active`、`full`、`unavailable`。受管内容包含后台上传普通贴纸原图和缩略图,不含只读规范素材;`full/unavailable` 阻断上传和缩略图生成,清理后的容量状态只在物理清理和重新计量后更新,详见第 8 节 | +| 历史上传贴纸清理 | 只允许 `super_admin` 确认并显式清理不再被任何已发布版本或项目引用的原图/缩略图;仍有引用时拒绝整个操作,清理请求、校验和结果均审计,物理清理与重新计量前不更新容量状态 | + +### 6.1 九类生成错误 + +| 分类 | 用户动作 | 任务与点数 | +| --- | --- | --- | +| `upstream_timeout` | 使用原输入重试 | `failed`,释放冻结点 | +| `upstream_failed` | 稍后重试 | `failed`,释放冻结点 | +| `safety_rejected` | 修改提示词或参考图 | `rejected`,释放冻结点 | +| `model_disabled` | 选择其他模型或等待 | 不创建任务、不冻结点 | +| `gateway_balance_insufficient` | 选择未受影响模型或联系管理员 | 未创建则不冻结;已创建则 `failed` 并释放 | +| `gateway_contract_invalid` | 选择其他模型或联系管理员 | 未创建则不冻结;已创建则 `failed` 并释放 | +| `reference_invalid` | 更换或移除参考图 | 未创建则不冻结;已创建则 `failed` 并释放 | +| `unknown_retryable` | 稍后重试 | `failed`,释放冻结点 | +| `unknown_non_retryable` | 联系管理员 | `failed`,释放冻结点 | + +`reference_invalid` 沿用 PRD 的“参考图格式、大小、数量或内容无效”边界;供应商安全策略拒绝提示词或参考图时使用 `safety_rejected`。三个模型共用固定分类。P0 使用统一 `config_version`,错误映射变化递增该版本,不拆分错误映射版本;后台不能新增、重命名或删除分类。 + +## 7. 十九个产品数据契约导航 + +本节只说明用途,不复制或重新设计字段。 + +| 契约 | 用途 | +| --- | --- | +| `UserProfile` | 账号身份、资料、角色、状态、注册告知同意、`super_admin` 最近确认的私有内容告知版本与时间,以及注销时间的产品记录 | +| `ModelConfig` | 每模型启停、唯一已启用默认模型、唯一正整数推荐优先级、点数、输入限制、网关路由、契约验证和统一版本复核 | +| `Project` | 项目比例、底图、生成历史、画布、最新成品、版本和保存状态;同时表达 `draft_state`、`batch_deletable`、回收站时间及清理边界 | +| `GenerationJob` | 每次生成的配置快照、任务状态、参考图关系、点数和错误分类 | +| `CanvasElement` | 文字、普通贴纸、色卡和动态贴纸的通用画布状态与类型专用数据 | +| `TemplateRegistry` | 复杂模板稳定 ID、资源版本、验证、批次、发布层级和可见状态 | +| `StaticStickerCatalog` | 普通贴纸稳定 ID、既有 part、顺序、资源引用、版本、启停状态与 `bundled_read_only/admin_uploaded` 来源分类;后台上传文件受容量计量和无引用显式清理规则约束 | +| `DynamicStickerData` | 动态贴纸类型、原始/格式化值、覆盖值及 DYN004 授权坐标 | +| `CreditAccount` 与 `CreditLedger` | 可用/冻结余额,以及初始化、管理员调点、冻结、提交和释放的不可变流水 | +| `PrivateContentAccessLog` | `super_admin` 每次打开私有图片或完整提示词时产生的独立 180 天审计,不因全局提示已确认而省略 | +| `AnonymousRetainedEvent` | 注销后保留的不可回溯随机主体事件和有限统计字段 | +| `AssetReleaseManifest` | 版本化素材清单、哈希、资源类别与 P0-A `local_filesystem` 发布引用 | +| `AssetPreviewGrant` | 普通测试账号对指定内部模板批次的限时授权与撤销 | +| `ClientCachePolicy` | 公开素材主动缓存 150 MB 上限、LRU 和私有内容禁存规则 | +| `ExternalServiceUsage` | Resend/高德的周期用量、硬上限、暂停原因和恢复状态 | +| `AdminOperationLog` | 除私有内容查看外的后台敏感操作、普通贴纸历史文件清理请求/引用校验/结果,以及系统配置生效审计 | +| `GatewayBalanceState` | 网关余额、影响范围、运行时不可用模型和人工确认恢复;不改写模型启停、默认或推荐优先级 | +| `GenerationErrorCategory` | 三模型共享的固定错误、用户动作、重试和点数口径 | +| `LocalBackendStorageState` | P0-A 本机受管内容容量、80%/90% 提醒、5 GB 硬上限、可用状态和重计量时间;计入后台上传贴纸原图/缩略图,不计只读规范素材,清理完成后才更新状态 | + +## 8. 本机数据与资源访问边界 + +### 8.1 `LocalDataRoot` + +- `LocalDataRoot` 默认位于当前 Windows 用户的应用数据目录,由本地后端解析;必须可配置,且不得位于 Git 仓库、项目目录、用户主动下载目录或只读素材根目录。 +- 本机数据库、账号、项目、参考图、生成图、导出成品、审计日志、派生文件,以及后台上传的普通贴纸原图和 Dada 生成的缩略图写入 `LocalDataRoot`。浏览器只通过资源 ID 和本地后端鉴权接口访问私有文件。 +- 浏览器不得通过 `file://`、本机绝对路径或 File System Access API 读取 `LocalDataRoot` 或素材根目录;用户主动用标准文件选择器选择参考图不受此限制。 +- 未保存画布、提示词、私有图片引用和编辑内容只存在当前页面内存,不写入 `localStorage`、`IndexedDB` 或 Cache Storage。强制刷新、关闭或崩溃后只恢复最后一次本地后端 `saved` 状态。 +- P0-A 不创建自动备份,不提供可编辑项目包、数据库、账号、点数、审计日志或素材关系的导出/导入,也不把本机业务数据迁移到 P0-B。用户只能主动下载原始生成图和 JPG/PNG 成品。 +- P0-A 不对本机文件实施 Dada 应用层加密,依赖当前 Windows 用户登录和文件系统权限;机器损坏、重装或删除 `LocalDataRoot` 后不可由 Dada 恢复。 + +### 8.2 容量与可用性 + +`hard_limit_bytes` 固定为 5,368,709,120 字节,对用户显示 5 GB。`managed_content_bytes` 统计参考图、生成图、导出成品、派生二进制文件,以及 `super_admin` 后台上传的普通贴纸原图和 Dada 生成的缩略图;不统计产品随安装包提供或通过本机安全配置原地引用的只读规范素材库、应用代码、150 MB 浏览器公开素材缓存、数据库元数据、审计记录或用户下载目录。 + +| 条件 | 状态与行为 | +| --- | --- | +| 低于 80% 且目录可写 | `normal` / `active`,正常写入 | +| 达到 80% 且低于 90% | `warning`,非阻断提醒;继续允许生图、保存、后台贴纸原图上传和缩略图生成,不自动删除 | +| 达到 90% 且低于 5 GB | `critical`,持续清理提醒;仍允许生图、保存、后台贴纸原图上传和缩略图生成,不自动删除 | +| 已达到或本次写入预计超过 5 GB | `full`;不创建新任务、不调用 AI、不冻结点数,不持久化新参考图、`latest_exports`、后台贴纸原图、缩略图或其他受管二进制内容;仍可编辑已有项目、读取和下载已有内容、只下载客户端导出、永久删除项目及显式清理符合条件的无引用历史贴纸文件 | +| `LocalDataRoot` 不可写或系统磁盘已满 | `unavailable`;停止新增写入、后台贴纸上传、缩略图生成和 AI 调用,保留已有项目及素材的查看、下载和删除入口 | + +`warning` 和 `critical` 的清理引导顺序固定为:下载需保留的成品、永久删除不需要的项目或由 `super_admin` 显式清理符合条件的无引用历史贴纸文件、等待物理清理和重新测量、恢复后继续生成。历史上传原图和缩略图不得自动清理;只有不再被任何已发布版本或项目引用时,才能由 `super_admin` 确认后显式清理,清理请求、引用校验与结果必须写入不可修改的 `AdminOperationLog`,仍有引用时拒绝整个操作。只有物理清理完成并重新计量后,才能更新 `managed_content_bytes`、`capacity_notice_level`、`storage_status` 和 `last_measured_at`;容量低于硬上限且目录可写时才能恢复 `active`。 + +### 8.3 素材原地只读复用 + +P0-A 通过本地后端安全配置解析以下 `%USERPROFILE%` 路径。解析后的绝对路径不得进入 Git、普通项目数据或浏览器响应。 + +| 素材 | 只读入口 | +| --- | --- | +| 总交接 | `%USERPROFILE%\Desktop\sticker_web_handoff\STICKER_WEB_REPLICATION_HANDOFF.md` | +| 机器总 manifest | `%USERPROFILE%\Desktop\sticker_web_handoff\sticker_web_catalog_manifest.json` | +| 完整性报告 | `%USERPROFILE%\Desktop\sticker_web_handoff\sticker_archive_validation_20260722.json`,当前 `all_valid=true` | +| 文字模板 | `%USERPROFILE%\Desktop\sticker_text\模板\单模板归档`;分类 `catalog.csv` 与 `templates//metadata.json` | +| 字体 | `%USERPROFILE%\Desktop\sticker_text\字体\面板全量采集\font_panel_full_20260722`;`reports/font_panel_catalog.csv` 与单字体 `metadata.json` | +| 文字行为资料 | `%USERPROFILE%\Desktop\sticker_text\键盘`、`%USERPROFILE%\Desktop\sticker_text\样式` | +| 普通贴纸 | `%USERPROFILE%\Desktop\贴纸素材`;`sticker_part1` 至 `sticker_part25`,共 1,407 张 PNG | +| 色卡 | `%USERPROFILE%\Desktop\sticker_colour\单模板归档`;`catalog.csv` 与 `styles//metadata.json` | +| 动态贴纸 | `%USERPROFILE%\Desktop\sticker_interactive\单模板归档`;`catalog.csv` 与 `templates//metadata.json` | + +约 3.57 GB 规范素材在原目录只读复用,不复制全库、不提交 Git、不放入 `LocalDataRoot`,也不计入 `managed_content_bytes`。后台上传的普通贴纸原图和缩略图写入 `LocalDataRoot` 并计入容量,不得写回只读素材根目录。Git 只保存版本化 manifest、catalog、metadata、版本号和哈希。普通贴纸当前缺少统一 catalog,开发准备需生成 `StaticStickerCatalog`,但不得改写源素材。目录移动、改名、不可读或校验失败时,只暂停依赖功能并提示重新配置,不修改代码或自动复制素材。 + +## 9. P0-A 验收分类与发布准备 + +### 9.1 第 22 节 AC 分类 + +| 分类 | AC 范围 | `DevelopmentPlan.md` 处理规则 | +| --- | --- | --- | +| P0-A 必验 | AC-01 至 AC-07、AC-09 至 AC-14、AC-16 至 AC-22、AC-24 至 AC-25、AC-27 至 AC-35、AC-39 至 AC-53、AC-55 至 AC-56 | 必须拆入当前开发与验收任务 | +| 通用规则,当前只验 P0-A 子集 | AC-15、AC-23、AC-36、AC-38 | 只拆入下表列出的本机操作和预期 | +| P0-B 门槛与未来验收 | AC-08、AC-26、AC-37、AC-54 | 不属于当前交付,不得拆入当前迭代或阻止 P0-A 完成 | + +AC-15、AC-23、AC-36、AC-38 的完整规则仍保留在 PRD;本节只筛选当前 P0-A 验收子集,并未删除其余规则。 + +四个通用 AC 的当前子集为: + +| AC | P0-A 当前子集 | +| --- | --- | +| AC-15 | 仅验 COLOR001、COLOR002、COLOR008、COLOR016 四种色卡及其共同提色、换底图更新和原始底图边界 | +| AC-23 | 在四种比例导出 JPG/PNG,验证像素、画布一致性、未提交修改确认、每格式最新一份和同一电脑再次下载 | +| AC-36 | 仅验同一台 Windows 电脑两个标签页的 `state_version` 冲突、旧页只读、一次本地导出和刷新本地后端最新版本 | +| AC-38 | 仅验本机“暂无导出成品”空状态、进入编辑并导出、下载原始生成图,以及不自动创建默认成品 | + +### 9.2 P0-A 启动门槛 + +AC-41 必须在实际验收开始前通过,重点包括: + +- 本地网页和本地后端可启动,后端只监听 localhost,局域网地址和其他设备不能访问。 +- 第 18 节素材根目录可读,manifest 与素材文件校验通过,且未复制第二套约 3.57 GB 全量素材。 +- `LocalDataRoot` 独立、可写、未硬编码用户名;80%/90% 预警和 5 GB 限制可用。 +- 正式验收账号真实调用 Resend;发信域名验证完成,QQ、163、企业邮箱各 20 封至少 19 封在 2 分钟内送达;应用硬上限为每天 80 封、每月 2,400 封,不配置付费备用通道。 +- 高德个人 Web 服务 Key 的接口、绑定、安全限制和每月 1,000 次应用硬上限实测通过。 +- AI 网关不开自动充值,三个模型的 AC-40 完整契约矩阵通过,模型级/账户级/未知影响范围的余额停用与恢复规则可用。 +- 版本化内测告知、本机数据风险提示、150 MB 公开素材缓存上限和私有状态禁存通过。 +- AI 网关、Resend、高德所需外网 HTTPS 调用可用,任何 API 密钥均未进入 PRD、Git、前端、manifest 或本机数据日志。 + +任一启动门槛失败时不得开始 P0-A 实际验收。开发初始化账号只用于并行开发,不能替代 AUTH-01、AUTH-02、AUTH-07、AC-01、AC-33、AC-41 或 AC-49。 + +## 10. P0-B 未来开放门槛 + +P0-B 仅作为未来阶段的不可弱化门槛记录,不是当前功能或当前完成条件。未经未来单独立项,不得纳入当前开发任务。向普通用户开放前必须同时满足: + +- P0-A 验收已通过。 +- 全量 332 个文字模板、86 个字体面板项、1,407 张初始规范普通贴纸、16 个色卡和 35 个动态贴纸完成规定验收。 +- AC-26 全量跨平台视觉回归和 AC-37 手机横竖屏完整编辑通过。 +- 所有白名单外复杂模板均为 `passed`,随后一次性切换为 `enabled`,不得逐批向普通用户开放。 +- 普通账号阶段上限切换并验证为 50;公开 manifest 始终不含 `internal_preview_asset`。 +- 三个模型在最后一次契约相关配置变化后均为 `verified`。 +- 公司系统合并、正式存储后端、远程服务器、域名、HTTPS、正式数据隔离和移动端访问方案均已定版并验收。 +- P0-A 本机账号、项目、点数、审计日志和素材关系不自动或手动导入 P0-B;用户下载的图片不是迁移包。 + +不能通过只扩大人数、只开放部分模板或只完成手机编辑来宣称进入 P0-B。AC-08、AC-26、AC-37、AC-54 及 AC-15/23/36/38 中的 P0-B 子集须在未来单独立项。 + +## 11. 后续 DevelopmentPlan 输入约束 + +以下条目仅作为 `DevelopmentPlan.md` 必须处理的约束索引。它们不代表实现顺序、技术选型或任务拆分;`DevelopmentPlan.md` 必须在完整阅读 PRD v1.10 和本文件后编写,并至少处理以下工程主题: + +1. localhost 单机部署边界、启动方式、本地后端回环监听、进程与故障恢复,不引入 P0-B 基础设施。 +2. 邀请码、Resend 验证码、30 天会话、管理员白名单、多 `super_admin`、开发初始化账号隔离和正式验收切换。 +3. 点数事务、任务幂等、`queued/running` 单任务约束、提交时 `ModelConfig` 复核、默认模型原子替换、`recommendation_priority` 与运行时推荐、结算与失败释放。 +4. 三模型独立适配器、脱敏契约证据、同步/异步差异、参考图生命周期、统一 `config_version` 和九类错误映射。 +5. 项目/历史/失败草稿、自动保存、同机多标签冲突、撤销重做、回收站 720 小时与资源异步清理。 +6. Chrome/Edge 支持版本硬检查与无绕过阻断、画布渲染、字体加载、导出一致性、50 元素性能预算及固定浏览器版本、Windows 版本、DPR、视口和动画基准;具体环境在后续 `tdd.md` 记录。 +7. 只读素材根目录发现、manifest/catalog/metadata 校验、`StaticStickerCatalog` 生成、`asset_origin`、后台上传贴纸原图/缩略图、无引用显式清理、资源版本化、三类访问隔离和浏览器资源 ID 接口。 +8. `LocalDataRoot` 布局、`LocalBackendStorageState` 计量与写入前预判、后台贴纸容量归属、80%/90% 提示、5 GB 硬停止、`full/unavailable` 上传与缩略图阻断、物理清理后重新计量、数据库/日志目录不可写、日志轮转和 180 天清理。 +9. JPG/PNG 单次合成、下载与本地后端持久化一致性、失败降级及“只下载图片、无项目归档/迁移”的产品边界。 +10. Resend、高德和 AI 网关的免费或余额硬停止、运行时不可用不回写模型默认/推荐配置、无自动费用、管理员确认恢复及审计。 +11. 私有资源鉴权、按 `super_admin` 身份与告知版本持久确认私有内容提示、逐次访问审计、注销删除、匿名保留、密钥隔离和本机数据风险告知。 +12. 严格按第 22 节 AC 分类进行任务拆分和测试分层;当前只实现 P0-A 必验项及 AC-15/23/36/38 的 P0-A 子集。 + +素材授权台账在正式公开或商业化前补齐,不改变当前 P0-A 或未来 P0-B 的既定阻塞规则。后续文档不得把 P0-B 门槛弱化为分批开放,也不得把未来能力写入当前 P0-A 实现。 diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000..b52bdb6 --- /dev/null +++ b/PRD.md @@ -0,0 +1,3008 @@ +# Dada 产品需求文档 + +| 项目 | 内容 | +| --- | --- | +| 产品代号 | Dada | +| 文档类型 | 产品需求文档(PRD) | +| 文档版本 | 1.10 | +| 文档状态 | P0-A 本机测试需求冻结稿 | +| 更新日期 | 2026-07-24 | +| 首发形态 | P0-A 仅在项目方当前 Windows 电脑上以 localhost 单机运行并验收;P0-B 为与公司系统合并后的远程受控内测 | +| 当前交付范围 | 仅 P0-A;P0-B 只保留未来开放门槛和不可弱化约束,不属于当前开发交付 | +| 首发语言 | 简体中文 | + +## 1. 文档定位 + +本文件定义 Dada 第一阶段产品的目标、范围、用户流程、功能要求、业务规则、产品数据契约、异常状态和验收标准。 + +本文件不包含: + +- 技术架构、框架、数据库和部署方案; +- UI 配色、视觉规范和页面高保真设计; +- TDD、详细测试用例、开发排期和任务拆分; +- 任何 API 密钥、访问令牌或其他凭据。 + +后续文档分别为 FeatureSummary.md、DevelopmentPlan、UIDesign.md、tdd.md 和 tasks.md。若后续文档与本 PRD 冲突,以本 PRD 的产品范围和业务规则为准,除非本 PRD 正式升版。 + +后续文档不得把 P0-A 描述为云端保存、对象存储、跨设备继续处理、手机访问或任一设备再次下载。P0-A 统一使用“本地后端保存”“同机访问”和“同机再次下载”等阶段准确措辞。FeatureSummary.md 与 DevelopmentPlan.md 必须声明其输入 PRD 版本;PRD 升版后,两份下游文档在重新同步并冻结前不得作为开发或设计基准。 + +## 2. 产品概述 + +### 2.1 产品目标 + +Dada 是一个面向小红书内容创作者的图片创作工具。P0-A 以本机网页、本地后端和本机磁盘组成的单机测试版运行;用户可以先通过 AI 生成一张图片,再在同一项目中添加、编辑文字模板与贴纸,最终导出符合常用小红书图片比例的高清 JPG 或 PNG。P0-B 才进入与公司系统合并后的远程部署阶段。 + +P0 必须跑通以下闭环: + +1. 用户通过邀请码和邮箱验证码注册; +2. 用户选择模型、输入提示词,并可按模型能力添加参考图; +3. 系统生成一张图片并保存到项目历史; +4. 用户在当前阶段支持的网页编辑器中调整底图,添加文字和贴纸; +5. 项目自动保存; +6. 用户导出无水印高清 JPG 或 PNG; +7. 用户可在当前阶段支持的环境继续处理项目或重新下载成品。 + +### 2.2 核心用户 + +核心用户为需要快速制作小红书封面或笔记配图的个人内容创作者。 + +典型需求: + +- 不依赖专业设计软件完成一张可发布图片; +- 在多个图片模型之间选择; +- 使用接近小红书操作逻辑的文字模板、字体和贴纸; +- 保留可编辑项目并再次下载成品。 + +### 2.3 核心价值 + +- 将 AI 生图和社交图片编辑合并为连续工作流; +- 提供已归档的文字、字体、普通贴纸、色卡和动态贴纸; +- 保留图层级可编辑状态,而不是只保存合成图; +- 使用额度、并发和项目数量限制控制内测成本。 + +### 2.4 成功标准 + +P0 的首要成功标准是完整任务成功率,即测试用户可以在不依赖人工代操作的情况下完成注册、生成、编辑、保存和导出。 + +P0 分为两个发布门槛: + +- P0-A 本机测试:仅在项目方当前 Windows 电脑运行,由项目方控制的 1 个超级管理员账号和 1 个普通账号完成人工验收,普通账号系统上限为 10,只向普通用户展示已经通过首轮验收的复杂模板白名单;跨普通用户隔离由测试程序临时创建第二个普通账号验证,不要求朋友或第二个真实用户参与; +- P0-B 公司合并后阶段:最多 50 个普通账号,全部 332 个文字模板、86 个字体面板项、1,407 张普通贴纸、16 个色卡和 35 个动态贴纸均通过全量验收,并完成正式存储、远程部署、数据隔离与移动端能力验收。 + +P0 不设“必须在几分钟内完成”的产品目标,也不承诺公开服务 SLA。核心白名单门槛和完整 P0 门槛分别以本文件对应场景全部通过为准。 + +## 3. 发布阶段与优先级 + +### 3.1 P0-A:本机核心白名单测试 + +P0-A 只在项目方当前 Windows 电脑运行本地网页、本地后端和本机磁盘存储。本地后端只监听 localhost 回环地址,不监听局域网网卡地址;不支持手机、局域网、远程访问或朋友远程注册,也不使用 COS、公司对象存储、公司服务器、备案域名或 HTTPS。P0-A 由项目方控制的 1 个超级管理员账号和 1 个普通账号完成人工验收,不要求朋友参与,也不要求招满 10 人。系统最多开放 10 个普通账号;超级管理员不占普通账号名额。跨普通用户的数据隔离和资源越权由测试程序在同一电脑的隔离浏览器配置中临时创建第二个普通账号完成,测试结束后清理该账号。P0-A 必须跑通完整产品闭环,但只向普通用户展示以下已经验收的复杂模板: + +- 文字模板:FLOWER001 至 FLOWER008、H001 至 H008、TAG001 至 TAG007、TAG051、SIMPLE001 至 SIMPLE008,共 32 个; +- 字体:上述白名单模板实际引用的全部字体面板项,并强制包含 FONT081; +- 色卡:COLOR001、COLOR002、COLOR008、COLOR016; +- 动态贴纸:DYN001、DYN002、DYN003、DYN004、DYN007、DYN008、DYN011、DYN012、DYN015、DYN016; +- 普通贴纸:part1 至 part25 的全部 1,407 张 PNG。 + +白名单外复杂模板必须完成导入和注册,但默认停用且不向普通用户展示。只有超级管理员明确指定的普通测试账号可以查看被授权的内部测试批次;该授权不授予后台、调点、私有内容查看或其他管理员权限。 + +白名单覆盖矩阵如下。一个模板可以同时覆盖多个机制,正式验收以对应资源和快照为准: + +| 白名单范围 | 主要覆盖理由 | +| --- | --- | +| FLOWER001 至 FLOWER008 | 花字资源结构、材质与装饰资源、缺失预览,以及 FLOWER007 的合法 parameter_only 类型 | +| H001 至 H008 | 多行默认文本、多字体、双 Prefab、多贴图与大资源包;其中 H005 覆盖默认换行,H006 覆盖四字体与双 Prefab | +| TAG001 至 TAG007、TAG051 | 标签结构、材质与贴图,以及 TAG051 的 mapped_zip_deduplicated 去重映射 | +| SIMPLE001 至 SIMPLE008 | 简约模板、双 Prefab、多材质、缺失预览与大资源包 | + +P0-A 同时包含: + +- 分开的邀请码注册和邮箱验证码登录入口、30 天登录状态; +- 创作署名与社交 ID; +- 初始点数、生成扣点和超级管理员调点; +- 三个指定图片模型; +- 文字加参考图的单图生成; +- 单张画布、项目历史和本地后端自动保存; +- Windows Chrome 和 Edge 中的完整编辑器; +- 高清 JPG/PNG 导出; +- 基础运营后台和私有内容访问审计。 + +P0-A 本地测试页或设置区域必须明确显示:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +### 3.2 P0-B:公司系统合并后的完整 P0 + +P0-B 是公司系统合并后的未来阶段。本版仅定义其对普通用户开放前必须满足的门槛和不可弱化产品约束;P0-B 的功能实现、排期和验收不属于当前 P0-A 必交付范围,也不得阻止 P0-A 完成。未经未来单独立项,不得因为本节保留手机完整编辑、远程部署或正式存储要求,就把这些能力纳入当前开发任务。 + +完整 P0 最多开放 50 个普通账号。除 P0-A 的全部能力外,必须完成: + +- 全部 332 个文字模板与 86 个字体面板项的真实资源渲染; +- 全部 16 个色卡和 35 个动态贴纸的参数渲染; +- 全量视觉回归、人工复核和支持浏览器验收; +- 与公司系统完成合并,并定版正式存储后端、远程服务器、域名、HTTPS、数据隔离和移动端访问; +- 手机竖屏和横屏完整编辑器; +- 白名单外复杂模板允许在后台和内部测试账号中分批验收、分批标记通过,但所有剩余复杂模板全部通过后才一次性向普通用户启用,不逐批公开。 + +P0-B 的以下条件是未来对普通用户开放门槛。未来单独立项后可以提前进行内部开发、导入、预览和验收;在当前 P0-A 交付中不要求提前实施。扩大普通账号上限并向普通用户开放全部复杂模板前,必须同时满足: + +- P0-A 全部适用验收场景已经通过; +- 332 个文字模板、86 个字体面板项、1,407 张普通贴纸、16 个色卡和 35 个动态贴纸完成全量验收; +- AC-26 全量模板视觉回归和 AC-37 手机横竖屏完整编辑通过; +- 白名单外复杂模板全部为 passed,正式开放操作将这些模板一次性切换为 enabled,不逐批公开; +- 普通账号阶段上限已经配置并验证为 50; +- 浏览器公开 manifest 不包含 internal_preview_asset; +- 三个模型在最后一次契约相关配置变化后,contract_validation_status 均为 verified。 +- 公司系统合并方案、正式存储后端、远程服务器、域名、HTTPS、正式数据隔离和移动端访问均已定版并通过验收;P0-A 本机测试数据不迁移。 + +任一条件不满足时系统继续保持 P0-A 的普通用户可见范围和 10 个普通账号上限,不能只扩大人数或只开放部分模板来宣称进入 P0-B。 + +### 3.3 P1:内测后的明确增强 + +P1 包含: + +- 上传本地图片作为编辑底图; +- 新建纯色或渐变空白画布; +- 为普通静态贴纸补充标题和关键词后开放搜索; +- 移除邀请码前的公开注册准备; +- 从个人认证高德 Key 迁移到公司主体的正式运营评估; +- 面向公开注册的正式地区合规方案和完整条款升级。 + +### 3.4 明确不做 + +以下功能不属于 P0 或当前已承诺的 P1: + +- 支付、充值、订阅、订单和退款; +- 网页内直接发布到小红书; +- 小红书账号绑定; +- 多页图组、批量画布和批量导出; +- 公开作品广场、评论、点赞和社区推荐; +- 独立普通文字入口; +- 用户上传自定义贴纸; +- AI 生成贴纸; +- 动态贴纸、GIF 或视频导出; +- 笔记标题、正文和话题标签生成; +- 整版封面或海报模板; +- AI 提示词优化; +- 局部重绘、擦除、扩图和背景移除; +- 专业调色工具,如曲线、HSL、蒙版和局部调整; +- 实时多人协作; +- 项目版本历史; +- 文字或贴纸编组; +- 图层面板、元素隐藏和元素锁定。 + +## 4. 用户与权限 + +### 4.1 访客 + +通过浏览器支持检查的访客可以: + +- 查看登录和注册入口; +- 输入邮箱验证码完成登录; +- 使用有效邀请码注册。 + +访客不能: + +- 调用生图模型; +- 查看任何用户项目; +- 访问编辑器或运营后台。 + +### 4.2 注册用户 + +注册用户可以: + +- 使用个人点数调用已启用模型; +- 创建、编辑、导出、删除和恢复自己的项目; +- 管理创作署名和社交 ID; +- 查看个人点数余额和点数变动; +- 注销账号。 + +所有项目默认私有。其他普通用户不能通过项目 ID、图片地址或搜索访问这些内容。 + +### 4.3 超级管理员 + +P0 只有 super_admin 一种后台角色,允许存在多个后台账号,且所有后台账号权限相同。P0-A 管理员邮箱由本地后端安全配置中的白名单决定,具体邮箱不得写入 PRD、Git、前端代码或前端配置。超级管理员不开放自助注册,不使用普通邀请码,也不能由普通账号提升而来;详细创建、登录、停用和恢复规则见 AUTH-07。 + +超级管理员可以: + +- 查看和搜索用户; +- 调整用户点数; +- 暂停或恢复用户账号; +- 创建、停用和限制邀请码; +- 查看生成任务状态和用量; +- 启用、停用和配置模型; +- 上传与管理普通静态贴纸; +- 调整复杂模板的启停、分类顺序和展示顺序; +- 按需查看用户图片和完整提示词。 + +生成记录列表默认只显示元数据。超级管理员可以直接打开私有图片或完整提示词,不要求二次确认或填写原因;每次打开都必须自动记录管理员、时间、目标记录和查看内容类型。审计记录在 180 天保留期内不可修改或人工删除,到期后由系统清理。 + +超级管理员首次进入私有内容查看区域时必须看到一次全局规则提示。确认状态按“super_admin 身份 + 告知版本”持久记录,不按浏览器、标签页或会话记录;同一身份确认同一版本后不重复显示,告知文案升版后必须重新提示。每个查看入口必须明确标注“打开并记录审计”。全局提示确认不替代逐次自动审计,也不增加逐次填写原因的要求。 + +## 5. 信息架构与主流程 + +### 5.1 未登录入口 + +任何访问者进入 Dada 产品功能前,系统必须先检查当前浏览器是否属于 P0-A 支持环境。只有运行本地后端的同一台 Windows 电脑上的当前稳定版 Chrome 和 Edge 可以继续进入登录、邀请码注册或其他产品入口。 + +其他浏览器或不受支持的浏览器版本必须直接进入“浏览器不受支持”引导页。该页面只说明受支持的浏览器及使用受支持浏览器重新打开 localhost 的方式,不提供“仍然继续”入口;阻断状态下不得进入登录、注册、项目、编辑器、运营后台或任何其他产品功能。本规则不增加手机、局域网、远程或跨设备支持。 + +通过浏览器检查后,未登录用户首先看到登录或邀请码注册入口,不要求独立营销落地页。 + +### 5.2 登录后工作台 + +登录后默认进入创作工作台,工作台包含: + +- AI 生图输入区; +- 当前模型和本次点数消耗; +- 当前点数余额; +- 最近项目入口; +- 进入全部项目列表的入口。 + +### 5.3 新建项目流程 + +1. 用户输入提示词; +2. 用户选择模型; +3. 用户选择画布比例; +4. 用户按当前模型能力添加参考图; +5. 用户确认本次点数消耗; +6. 用户提交生成; +7. 系统创建草稿项目和生成任务; +8. 生成成功后,结果加入项目生成历史; +9. 用户可选择“继续编辑”进入本机完整编辑器,或下载原始生成图。 + +“新建创作”入口永远创建新项目,不默认追加到最近项目。 + +### 5.4 项目内继续生成 + +1. 用户从已有项目选择“继续生成”; +2. 系统沿用该项目固定画布比例; +3. 用户选择支持该比例的启用模型并提交; +4. 成功图片追加到该项目生成历史; +5. 当前项目达到 10 张成功图时阻止追加,但不影响用户通过“新建创作”创建其他项目。 + +### 5.5 编辑流程 + +1. AI 结果作为底图进入画布; +2. 用户可裁剪、适配、定位和调色; +3. 用户从文字模板或贴纸分区添加元素; +4. 用户在画布上移动、缩放、旋转和调整层级; +5. 已提交的修改自动保存; +6. 用户导出 JPG 或 PNG。 + +### 5.6 设备与访问流程 + +P0-A 是单机单设备测试版: + +- 只能从运行本地后端的同一台 Windows 电脑通过 localhost 访问; +- 不显示手机、局域网、远程访问或跨设备继续编辑入口; +- 同一电脑可以使用独立 Chrome/Edge 浏览器配置分别登录 super_admin、普通账号和测试程序临时账号; +- 多标签页仍遵守 state_version 冲突规则,但 P0-A 不承诺跨设备会话或项目同步; +- 项目、生成图和成品只保存在本机;需要长期保留的原始生成图或 JPG/PNG 成品由用户主动下载; +- P0-A 不提供可编辑项目包、数据库、账号、点数、审计日志或素材关系的导出、导入或迁移,下载图片仅作为用户留存文件,不作为 P0-B 正式系统的数据迁移来源。 + +P0-B 才恢复远程和多设备产品能力: + +- 公司系统合并和远程部署完成后,受支持设备可通过正式域名访问; +- 手机竖屏和横屏支持与电脑等价的完整编辑,旋转屏幕不得丢失当前编辑状态; +- 桌面键盘操作必须有可发现的手机触控按钮或手势替代; +- 任一设备创建或编辑的项目可以在其他受支持设备继续处理,但必须遵守多端冲突规则。 + +## 6. 账号与认证需求 + +### AUTH-01 邀请码注册 + +P0 仅允许持有效邀请码的用户注册。注册和登录使用两个独立入口,不自动把用户静默切换到另一流程。 + +邀请码可以配置最大使用次数、有效期和启停状态。注册时: + +1. 发送验证码前校验邀请码当前有效; +2. 发送验证码前校验当前阶段占用名额的普通账号总量未达到 P0-A 的 10 人或完整 P0 的 50 人上限; +3. 验证码、注册资料和告知同意均通过后,再次校验邀请码和阶段账号上限; +4. 账号创建与邀请码使用次数递增必须在同一原子操作中成功; +5. 账号未创建时不消耗邀请码次数。 + +账号名额规则: + +- active 和 suspended 普通账号均占用当前阶段名额; +- deleted 普通账号释放名额,但历史累计创建数量继续保留在后台统计中; +- 预置或创建的 super_admin 不占普通账号名额,也不使用普通邀请码; +- 暂停账号不能通过释放名额绕过内测人数限制。 + +### AUTH-02 邮箱验证码 + +注册和登录均采用邮箱验证码,不使用密码,但分别从“注册”和“登录”入口发起。 + +业务规则: + +- 验证码一次性有效; +- 验证码有效期为 10 分钟; +- 同一邮箱 60 秒内不能重复发送; +- 连续失败或高频请求必须触发频率限制; +- 验证码、邀请码和会话令牌不得写入项目数据或日志正文。 + +认证状态按以下规则处理: + +| 入口与账号状态 | 产品行为 | +| --- | --- | +| 注册入口 + 未注册邮箱 + 有效邀请码 | 发送验证码,验证后允许创建账号 | +| 注册入口 + 已注册邮箱 | 不重复创建账号,明确提示前往登录 | +| 注册入口 + 已暂停邮箱 | 不创建账号,明确提示账号已暂停并联系管理员 | +| 登录入口 + active 账号 | 发送验证码,验证后创建会话 | +| 登录入口 + 未注册或已注销邮箱 | 不创建会话,明确提示前往注册 | +| 登录入口 + suspended 账号 | 不创建会话,明确提示账号已暂停并联系管理员 | + +已注销邮箱在注册入口中按未注册邮箱处理,可使用新的有效邀请码创建全新账号。 + +### AUTH-03 注册资料 + +注册时必须填写: + +- 创作署名:用于用户类贴纸中的展示昵称; +- 社交 ID:用于需要账号格式的贴纸,展示时最多保留一个前导 @; +- 勾选同意简短的《内测使用与隐私告知》。 + +创作署名和社交 ID 与 Dada 内部账号标识分离,用户可在账号设置中修改,也可在单个贴纸实例中临时覆盖。 + +### AUTH-04 登录状态 + +受信设备登录状态保持 30 天。以下情况必须立即失效: + +- 用户主动退出; +- 用户注销账号; +- 管理员暂停账号; +- 系统检测到会话无效。 + +### AUTH-05 账号暂停 + +账号暂停后: + +- 用户不能继续生成、编辑或导出; +- 已登录会话立即失效; +- 项目在账号恢复前保持私有保存; +- 超级管理员可以恢复账号。 + +### AUTH-06 账号注销 + +用户确认注销后立即执行: + +- 使所有会话失效; +- 删除账号资料; +- 删除所有项目、参考图、生成图、原始坐标和导出成品; +- 删除未使用点数; +- 无法恢复账号。 + +注销完成后,同一邮箱可使用新的有效邀请码注册全新账号,但不得恢复旧账号资料、项目、点数、署名或任何历史内容。 + +注销后的数据处理规则: + +- 邮箱、账号资料、提示词、参考图、生成图、项目、原始坐标和导出成品立即删除; +- 匿名生成和点数事件明细保留 180 天,可保留随机匿名主体、时间、模型、成功或失败、错误类别和点数变化; +- 匿名事件不得保留邮箱、IP、提示词、图片、项目 ID、生成任务原 ID或其他可恢复内容; +- 私有内容访问审计保留执行查看的超级管理员身份,被注销用户和目标记录替换为不可反推的随机标识,并在原访问事件发生 180 天后由系统删除; +- 匿名事件在注销后最多保留 180 天; +- 不可回溯到个人或单次作品的汇总统计可以长期保留。 + +### AUTH-07 超级管理员创建与登录 + +P0-A 的超级管理员由本地后端安全配置中的邮箱白名单控制。具体邮箱、验证码、会话令牌和恢复配置均不得进入 PRD、Git、前端代码、前端配置、素材 manifest 或本机数据日志。 + +本节所称“部署人员”不是 Dada 用户或后台角色,不进入 UserProfile.role,没有 Dada 登录入口、后台会话或产品权限。P0-A 中由项目方作为本机配置维护者管理本地后端安全配置;P0-B 中该职责由公司既有运维流程承接。配置变更在 Dada 读取并生效时,由系统写入不包含人员身份或安全配置内容的审计记录。 + +业务规则: + +- 超级管理员使用与普通用户分离的后台入口,并通过邮箱验证码登录,不使用密码; +- 后台在发送验证码前校验邮箱白名单,非白名单邮箱直接拒绝,不发送验证码,也不泄露完整白名单; +- 白名单邮箱首次完成验证码验证后创建 super_admin,后续从同一后台入口登录; +- 白名单允许包含多个邮箱;后台不提供管理员邀请、添加或普通账号提升功能,增加管理员只能由部署人员修改当前阶段的后端安全配置; +- 超级管理员不使用普通邀请码、不占普通账号上限,普通账号即使邮箱后来进入白名单也不能原地提升,必须由部署人员先解决账号身份冲突后再按受控管理员流程创建独立管理员身份; +- 管理员会话保持 30 天;主动退出、账号停用、邮箱被移出白名单或会话无效时立即失效; +- 任一超级管理员可以停用其他超级管理员,相关操作必须审计;系统不设置“最后一个管理员”保护,允许全部管理员被停用; +- 全部管理员被停用或移出白名单后,后台不提供自助恢复,只能由部署人员通过当前阶段的后端安全配置恢复受控管理员入口; +- 管理员账号创建、登录结果、停用和恢复均形成不可修改的后台操作审计;当前阶段后端安全配置中的管理员白名单、身份冲突处理或恢复配置生效时,以 system 身份形成审计,按 ADMIN-09 和 AdminOperationLog 保留 180 天。 + +## 7. 点数与额度需求 + +### CREDIT-01 初始点数 + +新注册用户默认获得 10 点。初始点数由后台配置,P0 默认值为 10。 + +每个账号同时维护: + +- available_balance:可用于新任务的点数,可因超级管理员调点变为负数; +- reserved_balance:已被运行中任务冻结的点数,只能由对应任务提交或释放。 + +### CREDIT-02 模型点数 + +每个模型的单次生成点数由后台独立配置。P0 初始默认均为 1 点,超级管理员可根据真实成本调整。 + +生成按钮附近必须在提交前显示: + +- 当前模型; +- 本次消耗点数; +- 当前可用点数; +- 当前冻结点数; +- 生成后的预计可用点数。 + +### CREDIT-03 原子扣点 + +提交生成时以原子操作把本次所需点数从 available_balance 转入 reserved_balance。 + +- 生成成功:对应点数从 reserved_balance 转为已扣点; +- 上游失败、超时或安全拒绝:对应点数从 reserved_balance 返还 available_balance; +- 页面关闭或刷新:不改变任务与冻结状态; +- 重复提交不能产生重复任务或重复扣点; +- 生成并发、重试和任务结算本身不得使 available_balance 变为负数。 + +### CREDIT-04 余额不足 + +available_balance 低于所选模型单次点数时: + +- 禁用生成提交; +- 明确显示所需点数、当前可用点数和冻结点数; +- 不创建上游任务; +- P0 仅提示联系管理员,不提供充值入口。 + +### CREDIT-05 点数流水 + +用户可以查看个人点数变动。每条记录至少包含: + +- 变动类型; +- 变动数量; +- 变动前后的 available_balance 和 reserved_balance; +- 关联模型或管理员操作; +- 管理员调点原因; +- 时间; +- 成功、释放或撤销状态。 + +### CREDIT-06 超级管理员调点 + +- 增加或扣减点数只修改 available_balance,不得修改 reserved_balance; +- 超级管理员可以把 available_balance 调整为负数; +- 负余额必须如实向用户显示,后续增加点数先抵扣负余额; +- available_balance 再次达到所选模型成本前,用户不能提交新生成; +- 每次调点必须记录超级管理员、原因、数量、调整前后余额和时间; +- 除超级管理员调点外,任何系统流程不得制造负余额。 + +## 8. AI 生图需求 + +### GEN-01 支持模型 + +P0 支持以下模型标识: + +| 模型标识 | P0-A 初始配置默认 | 选择方式 | +| --- | --- | --- | +| gemini-3.1-flash-image-preview | 是 | 用户手动选择 | +| gemini-3-pro-image-preview | 否 | 用户手动选择 | +| gpt-image-2 | 否 | 用户手动选择 | + +后台可以停用任何模型。停用后: + +- 新任务不能选择该模型; +- 历史记录仍显示原模型名; +- 已使用该模型生成的项目和图片保持可用。 + +后台配置必须始终存在且只存在一个已启用的默认模型。超级管理员主动停用当前默认模型时,必须在同一原子操作中指定另一个已启用模型作为新默认模型;替代模型缺失、未启用或不合法时,整个操作失败,原模型启停和默认状态均保持不变,不允许出现部分更新或无默认模型状态。 + +每个 ModelConfig 必须配置唯一正整数 recommendation_priority,数值 1 表示最高推荐优先级,不允许不同模型并列。后端只在当前运行时可用于新任务的模型中按推荐优先级由高到低(即数值由小到大)返回一个推荐模型;没有运行时可用模型时返回空推荐,并进入既有生成禁用或失败状态。运行时推荐是派生结果,不是第二个后台默认状态,也不得写回 ModelConfig。外部服务临时不可用、AI 网关余额不足、契约失效或其他运行时降级只改变运行时可用性,不得改写 ModelConfig.enabled、is_default 或后台配置的 recommendation_priority。具体 API 字段和响应结构由后续 DevelopmentPlan 同步,不属于本 PRD 的产品契约范围。 + +### GEN-02 统一基础输入 + +生图面板只提供所有模型都能映射的基础输入: + +- 提示词; +- 模型; +- 画布比例; +- 参考图; +- 生成按钮。 + +P0 不展示负面提示词、种子、采样器或模型专属高级参数。 + +### GEN-03 提示词 + +- 支持简体中文自由输入; +- P0 不提供模板、示例、自动补全或 AI 优化; +- 提示词长度上限由 ModelConfig 决定; +- 超出上限时在客户端阻止提交并说明限制。 + +### GEN-04 画布比例 + +P0 支持: + +| 比例 | 标准像素 | +| --- | ---: | +| 3:4 | 1080 × 1440 | +| 1:1 | 1080 × 1080 | +| 4:3 | 1440 × 1080 | +| 9:16 | 1080 × 1920 | + +3:4 为默认比例。 + +每个模型通过 ModelConfig 声明支持的比例。切换到不支持当前比例的模型时,系统必须要求用户选择该模型支持的比例,不能静默改动。 + +项目首次创建时确定 canvas_ratio 和 canvas_size,此后不可修改。项目内“继续生成”只能选择支持项目既有比例的模型;用户需要其他比例时必须通过“新建创作”创建新项目。 + +### GEN-05 参考图 + +- 参考图为可选项; +- 支持 JPG、PNG 和 WebP; +- 数量上限、单文件大小和总大小由当前模型配置; +- 切换模型后若现有参考图超过新模型上限,必须要求用户移除超出部分; +- 用户在提交生成前选择的本地参考图只存在于当前页面内存,不上传且不写入浏览器持久化存储; +- 点击提交生成时才上传参考图,并绑定到本次 GenerationJob; +- 每个成功、失败或被拒绝的 GenerationJob 保留提交时的参考图关系,不因后续重试而替换; +- 相同项目内重试时可以复用已有参考图资源,但每个任务仍保存自己的关系快照; +- 删除项目或注销账号时按对应规则删除参考图。 + +### GEN-06 每次结果 + +每次生成只返回并保存一张候选图。 + +### GEN-07 并发限制 + +同一账号同时只能有一个运行中的生图任务。任务运行时再次提交必须被阻止,并提供返回当前任务的入口。 + +### GEN-08 任务状态 + +生成任务状态为: + +- queued:已创建,等待调用; +- running:上游处理中; +- succeeded:已获得并保存图片; +- failed:技术失败或超时; +- rejected:上游安全策略拒绝。 + +页面刷新或切换设备后,用户仍能看到当前任务的真实状态。P0 不展示虚假的百分比进度。 + +### GEN-09 失败处理 + +失败状态必须: + +- 显示可理解的错误类别; +- 不暴露上游密钥、原始堆栈或内部地址; +- 释放冻结点数; +- 保留用户提示词和草稿项目; +- 允许用户在失败页或当前草稿中重新提交; +- 重试只新增 GenerationJob,不创建新项目。 + +### GEN-10 内容安全 + +P0 依赖模型供应商的安全策略,不建设人工内容审核流程。 + +当供应商拒绝请求时: + +- 任务状态为 rejected; +- 不保存违规输出; +- 不扣点; +- 向用户显示不包含敏感内部细节的拒绝提示。 + +### GEN-11 生成历史 + +每个项目最多保留 10 张成功生成图。 + +- 用户可删除单张历史图; +- 达到 10 张后必须先删除旧图才能在该项目继续生成; +- 工作台“新建创作”不受其他项目生成历史上限影响,但仍受 20 个项目上限约束; +- 当前作为底图的图片不能直接删除,除非先切换底图; +- 每张历史图保留模型、比例、提示词、参考图关系、生成时间和点数记录; +- 同一项目内所有历史图比例必须与项目固定比例一致。 + +### GEN-12 更换底图 + +切换到项目内另一张生成历史图时: + +- 保留所有文字和贴纸覆盖层; +- 元素的位置和尺寸按相同画布比例保留; +- 底图的裁剪、适配和调色状态重置为新底图默认状态; +- 已有色卡按 COL-04 重新提取; +- 切换动作进入撤销历史并触发自动保存。 + +### GEN-13 AI 网关契约验证门槛 + +取得网关图片 API 文档、脱敏调用示例或平台支持确认之前,可以并行开发编辑器、素材、认证等独立模块,但不得按猜测定版 AI 适配器、ModelConfig 路由、参考图协议或点数结算联调。 + +三个模型分别完成以下验证并保存脱敏证据后,才能解除 AI 契约阻塞: + +- 纯文字生成一张底图; +- 按模型能力使用参考图生成; +- 3:4、1:1、4:3、9:16 四种比例; +- 技术失败、超时和安全拒绝; +- 返回图片的格式、尺寸和获取方式; +- 请求是同步还是异步,以及异步任务查询方式; +- 错误映射、任务状态与冻结点数释放语义。 + +不得假设三个模型使用相同图片端点或参数格式。 + +模型端点、请求字段、参考图协议、响应结构、同步或异步模式、任务查询方式或上游错误协议发生变化时,必须立即把对应模型的 contract_validation_status 重置为 unverified,并重新执行 AC-40。显示名称、点数等不改变上游调用协议的配置变化不触发该重验规则。 + +### GEN-14 提交时配置复核 + +生图页面必须保存用户确认时的 model_config_version 和 confirmed_credit_cost。服务端收到提交时重新读取当前 ModelConfig,并核对模型启停、点数、支持比例、参考图限制和提示词限制。 + +- 当前配置与用户确认版本一致时,才允许创建任务并冻结点数; +- 点数、比例、参考图限制、提示词限制或启停状态发生变化时,本次提交必须拒绝; +- 拒绝时不创建 GenerationJob、不调用上游、不冻结点数; +- 页面显示最新配置并要求用户重新确认,不能静默按更高点数或不同限制继续; +- 已经成功提交的任务继续按提交时保存的配置快照结算。 + +### GEN-15 AI 网关余额影响范围 + +AI 网关余额不足不等同于 Dada 点数不足。系统根据网关返回结果和后台配置确定真实上游影响范围: + +- 能明确确认某一模型额度不足或仅该模型受影响时,只把该模型标记为运行时不可用于新任务; +- 只能确认共享网关账户余额不足,或无法可靠判断模型级影响范围时,把所有依赖同一网关结算账户的模型标记为运行时不可用于新任务; +- 因网关余额不足而失败的已提交任务必须进入失败状态,并且只释放一次对应 reserved_balance,不消耗用户点数; +- 运行时不可用后向超级管理员显示影响范围和检测时间,不得自动充值或用 Dada 点数继续制造无法支付的上游调用,也不得改写后台配置的默认模型、enabled 或 recommendation_priority; +- 只有确认真实网关余额已经恢复后,超级管理员才能恢复受影响模型的运行时可用状态,恢复操作必须审计。 + +### GEN-16 产品级生成错误分类 + +Dada 使用稳定的 GenerationErrorCategory 统一三个模型的提交和执行错误。上游原始错误码、堆栈和内部地址不得直接面向用户展示。失败或拒绝的 GenerationJob 必须保存 error_category;queued、running 和 succeeded 任务不保存错误分类。提交前被阻止时不创建 GenerationJob,但提交响应仍返回对应 error_category。 + +| error_category | 产品含义 | 用户下一步 | 任务与点数 | +| --- | --- | --- | --- | +| upstream_timeout | 上游超时 | 使用原输入重试 | failed,释放冻结点 | +| upstream_failed | 上游临时失败 | 稍后重试 | failed,释放冻结点 | +| safety_rejected | 供应商安全拒绝 | 修改提示词或参考图 | rejected,释放冻结点 | +| model_disabled | 模型因普通启停规则不可用 | 选择其他模型或等待 | 不创建任务、不冻结点 | +| gateway_balance_insufficient | 真实网关余额不足 | 选择未受影响模型或联系管理员 | 未创建则不冻结;已创建则 failed 并释放 | +| gateway_contract_invalid | 网关契约未验证或失效 | 选择其他模型或联系管理员 | 未创建则不冻结;已创建则 failed 并释放 | +| reference_invalid | 参考图格式、大小、数量或内容无效 | 更换或移除参考图 | 未创建则不冻结;已创建则 failed 并释放 | +| unknown_retryable | 未知但判断为临时错误 | 稍后重试 | failed,释放冻结点 | +| unknown_non_retryable | 未知且不应反复重试 | 联系管理员 | failed,释放冻结点 | + +已知禁用原因是网关余额或契约状态时,gateway_balance_insufficient 或 gateway_contract_invalid 优先于通用 model_disabled。用户点数不足、ModelConfig 版本变化、已有并发任务、项目上限、生成历史上限以及 P0-A 本机存储 full 或 unavailable 继续使用现有确定性业务阻止规则,不扩入本错误分类。 + +ModelConfig.error_mapping_profile 负责把各供应商的错误映射到上述固定分类。P0 后台可以调整上游错误映射,但不能新增临时分类;映射变化必须产生新的 config_version。UIDesign.md 可以确定中文文案,但不能改变分类对应的重试、修改输入、选择模型或联系管理员动作。 + +## 9. 项目与持久化保存 + +### PROJECT-01 项目创建 + +工作台“新建创作”首次提交时始终创建新的草稿项目。项目即使生成失败也保留提示词和当前配置,方便重试。失败页或该草稿中的“重试”复用原项目并只新增 GenerationJob;只有用户再次主动进入工作台“新建创作”才创建其他项目。 + +已有项目的“继续生成”只创建新的 GenerationJob,不创建新项目;成功结果追加到该项目历史,失败时保留原项目和本次配置。 + +没有任何成功生成图的失败草稿支持在项目列表中多选并批量移入回收站;包含成功生成图的项目不进入该快捷批量操作。 + +### PROJECT-02 项目命名 + +项目名称默认由提示词摘要和创建日期生成,用户可以随时重命名。 + +### PROJECT-03 项目数量 + +每个用户最多拥有 20 个 status=active 的项目。status=trashed 和 status=purged 均不占活动项目名额;达到 20 个 active 项目后阻止创建新项目。 + +### PROJECT-04 自动保存 + +自动保存对象为最新可编辑项目状态,而不是仅保存合成图。 + +自动保存发生在: + +- 文字或样式点击“完成”; +- 元素移动、缩放、旋转或多选移动结束; +- 元素添加、复制、删除或层级变化; +- 底图裁剪、调色或切换确认; +- 项目重命名; +- 撤销或重做完成。 + +保存队列遵守以下规则: + +- 一次已提交操作结束后等待 1 秒,将连续变化合并为当前最新项目状态; +- 同一项目同一时间最多一个保存请求在途; +- 请求在途期间的新变化合并到下一次最新状态,不逐条并发发送; +- 保存失败时显示“未保存”,页面保持打开期间在网络恢复后自动重试; +- 不把未保存画布、提示词、私有图片引用或编辑内容写入 localStorage、IndexedDB 或 Cache Storage; +- 刷新页面等同离开页面;强制刷新、关闭页面或浏览器崩溃后,只恢复最后一次后端 save_status=saved 的状态,不恢复未保存修改; +- 保存成功前不能继续显示“已保存”。 + +### PROJECT-05 离开页面 + +项目存在未提交或未保存修改时: + +- 站内跳转必须提供“保存并离开”“放弃修改”“取消”三个选项; +- 选择“保存并离开”后,必须提交当前编辑态并等待保存成功; +- 刷新或关闭页面时使用浏览器离开警告并尝试最后一次保存; +- 用户明确放弃、强制刷新、强制关闭或浏览器崩溃后,未保存修改丢失;重新打开项目时只加载最后一次后端 saved 状态。 + +### PROJECT-06 撤销与重做 + +- 仅保留当前编辑会话的多步撤销和重做; +- 重新打开项目后撤销栈为空; +- 一次连续拖拽作为一个操作; +- 一次文字编辑从进入编辑到点击完成作为一个操作; +- 取消编辑不进入撤销历史。 + +### PROJECT-07 多端冲突 + +P0 不支持同一项目多人协作。P0-A 在同一电脑的多个标签页编辑同一项目时必须比较 state_version;P0-B 扩展到多个设备。 + +旧页面检测到后端已有较新版本后: + +- 立即停止自动保存并进入只读冲突状态; +- 不允许继续编辑、覆盖后端状态或另存为项目副本; +- 允许用户把旧页面当前画面本地导出一次,导出结果不更新后端成品; +- 用户确认放弃本地修改后,刷新加载后端最新版本。 + +### PROJECT-08 删除与恢复 + +用户删除项目后: + +- 项目进入回收站; +- 系统按删除时间计算 purge_at = deleted_at + 720 小时,在 purge_at 前可以恢复; +- 到达 purge_at 后,用户侧立即视为 purged 且不可恢复,即使本机文件物理清理仍在异步队列中; +- 用户可以在 purge_at 前主动永久删除,确认后立即进入不可恢复的 purged 状态; +- purged 项目的项目数据、生成图、参考图、原始坐标和导出成品进入异步物理清理; +- 恢复操作只有在当前 active 项目少于 20 个时才能把项目从 trashed 改为 active;活动项目已达 20 个时阻止恢复,项目继续保持 trashed,原 deleted_at 和 purge_at 不变,用户必须先释放活动项目名额。 + +### PROJECT-09 项目可见性 + +- 项目默认且始终私有; +- P0 不提供公开链接; +- P0 不提供作品广场; +- P0-A 参考图、生成图和导出成品必须通过本地后端登录鉴权接口访问,不能从静态公开目录直接读取; +- P0-B 的正式私有资源访问方案必须在公司系统合并时重新定版; +- 私有资源地址不得通过猜测获得未授权访问。 + +## 10. 编辑器通用需求 + +### EDITOR-01 设备范围 + +P0-A 完整编辑器只支持运行本地后端的同一台 Windows 电脑上的 Chrome 和 Edge,不提供手机网页、局域网或远程访问能力。 + +P0-B 才正式支持手机完整编辑器: + +- 手机竖屏和横屏均可完成底图、文字、贴纸、多选、层级、撤销重做和导出; +- 屏幕旋转只改变布局,不提交、取消或丢失当前编辑状态; +- 手机使用适配小屏的面板、触控按钮和手势,不得以“请使用电脑”替代功能; +- 电脑键盘快捷键不要求手机模拟键盘,但必须有等价触控操作。 + +### EDITOR-02 画布与底图 + +底图支持: + +- 填充画布; +- 完整适应画布; +- 裁剪; +- 缩放; +- 定位; +- 从项目生成历史中更换。 + +P0 不能上传本地图片作为底图,也不能创建空白画布。 + +### EDITOR-03 底图调色 + +P0 提供: + +- 亮度; +- 对比度; +- 饱和度; +- 色温; +- 锐度; +- 预设滤镜; +- 一键恢复原图。 + +滤镜名称、缩略图和具体视觉值由 UIDesign.md 定义。PRD 要求所有调节可撤销、可保存并反映在导出结果中。 + +### EDITOR-04 元素上限 + +单张画布最多包含 50 个覆盖元素。覆盖元素包括文字模板、普通贴纸、色卡贴纸和动态贴纸,不包含底图。 + +达到上限后不能继续添加,必须提示用户删除现有元素。 + +### EDITOR-05 单选与多选 + +支持: + +- 点击或轻点优先选择命中区域中 z_index 最高的元素; +- 在同一点连续点击或轻点时,按 z_index 从高到低循环选择候选元素; +- 长按重叠区域时,显示仅包含该命中点候选元素的临时列表,通过文字内容、元素类型或缩略图区分; +- 桌面 Shift 点击追加或移除选择; +- 电脑提供可见的“多选模式”;P0-B 手机完整编辑器提供等价触控多选模式,用于逐个追加或移除选择; +- 框选多个元素; +- 多选元素整体移动、复制或删除。 + +选择循环在用户移动指针、开始拖拽或点击其他位置后重置。候选列表不是完整图层面板,不显示未命中该点的元素,也不提供全局排序。 + +不支持编组。多选状态下不能合并不同文字元素的样式编辑。 + +### EDITOR-06 画布变换 + +所有覆盖元素支持: + +- 移动; +- 等比缩放; +- 旋转; +- 复制; +- 删除; +- 置顶; +- 上移一层; +- 下移一层; +- 置底。 + +普通贴纸额外支持水平翻转和元素透明度。 + +### EDITOR-07 对齐辅助 + +移动元素时提供画布中心线、边缘和其他元素的吸附提示。辅助线只用于编辑,不进入导出图片。 + +### EDITOR-08 键盘操作 + +桌面端至少支持: + +- 撤销与重做; +- Delete 或 Backspace 删除选中元素; +- 方向键微移; +- Shift 加方向键大步移动; +- 复制与粘贴内部画布元素; +- 多选修饰键。 + +具体快捷键展示方式由 UIDesign.md 定义。 + +P0-B 手机完整编辑器必须为撤销、重做、删除、复制、层级调整和多选提供触控入口;需要拖拽的操作必须兼容单指触控,并避免与页面滚动无意冲突。 + +### EDITOR-09 不提供图层面板 + +P0 不提供常驻或展示全部元素的图层面板,不提供元素锁定或隐藏。长按命中点出现的候选列表不视为图层面板。所有元素必须能通过循环选择、候选列表或框选触达。 + +## 11. 文字系统 + +### TEXT-01 规范素材范围 + +完整 P0 接入全部 332 个文字模板: + +| 分类 | 数量 | 稳定 ID | +| --- | ---: | --- | +| 花字 | 145 | FLOWER001 至 FLOWER145 | +| 标题 | 119 | H001 至 H119 | +| 标签 | 51 | TAG001 至 TAG051 | +| 简约 | 17 | SIMPLE001 至 SIMPLE017 | + +其中 330 个为 ZIP 资源模板,FLOWER007“好柿花生”和 FLOWER025“分享瞬间”为 parameter_only 模板。它们没有 ZIP 是合法状态,不能按资源缺失处理。 + +### TEXT-02 添加文字 + +P0 添加文字必须先选择一个模板,不提供独立普通文字入口。 + +模板列表支持: + +- 按花字、标题、标签、简约分类; +- 按显示名称搜索; +- 按 catalog.csv 的稳定顺序展示; +- 记录账号级最近使用。 + +### TEXT-03 文字内容 + +- 支持输入和替换文字; +- 支持 UTF-8 多行文本; +- 保留换行符; +- 编辑中可以暂时为空; +- 点击完成时若内容为空,提示用户输入内容或删除该元素。 + +### TEXT-04 字体 + +完整 P0 展示 86 个字体面板项,对应 85 份唯一字体二进制。即使两个面板项共用同一字体文件,也保留各自稳定 ID 和面板顺序。 + +字体规则: + +- 模板自带字体用于模板默认视觉; +- 字体面板用于用户主动覆盖; +- 字体缩略图仅用于名称预览; +- 实际渲染必须加载归档字体文件; +- 用户选择字体后保留当前模板的其他样式。 + +有效字体规则为: + +effectiveFont = 用户主动选择字体,否则使用模板默认字体。 + +### TEXT-05 模板切换 + +已有文字元素切换模板时: + +- 保留文本内容和换行; +- 重置字体为新模板默认字体; +- 重置颜色、描边、背景、对齐、行距和字距为新模板默认值; +- 保持画布位置、旋转和整体视觉尺寸; +- 作为一次可撤销操作提交。 + +### TEXT-06 文字大小 + +用户可以: + +- 通过画布控制柄缩放文字; +- 通过数值字号精确设置; +- 两种操作保持同步; +- 文字框随内容、换行和字号重新测量,不能裁掉可见文字。 + +### TEXT-07 颜色 + +文字填充色、描边色和背景色均支持: + +- 参考预设色; +- 自定义色盘; +- 十六进制颜色输入。 + +已验证参考值包括: + +- 红色填充:#FA5751; +- 黑色描边:#000000; +- 亮黄色背景:#FFE62C。 + +### TEXT-08 描边 + +- 支持开启或关闭描边; +- 描边宽度范围为 0 至 12; +- 描边颜色与填充颜色独立; +- 描边变化不能下载或替换模板资源。 + +### TEXT-09 背景 + +- 支持开启或关闭文字背景; +- 背景颜色独立于文字填充; +- 背景透明度范围为 0% 至 100%; +- 背景透明度只影响背景,不影响文字; +- 复合预设可以一次更新填充、描边和背景。 + +### TEXT-10 对齐 + +支持三种水平对齐: + +- 左对齐; +- 居中; +- 右对齐。 + +### TEXT-11 行距 + +- 有效范围:1.0 至 1.9; +- 步长:0.1; +- 未显式修改时使用模板默认值; +- 归档中的 -1.0 是“未设置”哨兵,不能直接作为渲染值。 + +### TEXT-12 字距 + +- 有效范围:1.0 至 20.0; +- 步长:1.0; +- 未显式修改时使用模板默认值; +- 归档中的 -1.0 是“未设置”哨兵。 + +### TEXT-13 编辑提交 + +进入文字内容或样式编辑后: + +- 所有修改实时预览; +- 点击“完成”后形成一次撤销记录并触发自动保存; +- 点击“取消”恢复进入编辑前状态; +- 滑杆的每个中间值不单独进入撤销历史。 + +### TEXT-14 真实资源渲染 + +文字模板不能使用预览 PNG 代替真实渲染。必须依据模板 metadata.json、资源包、Prefab 转换结果、样式参数和字体文件还原。 + +## 12. 贴纸系统 + +### 12.1 总体结构 + +贴纸功能由三个相互独立的系统组成: + +- 普通静态贴纸库:固定为 part1 至 part25,共 25 个普通贴纸 part; +- 色卡贴纸系统:按 COLOR 稳定 ID 管理,不计入普通贴纸 part; +- 动态数据贴纸系统:按 DYN 稳定 ID 管理,不计入普通贴纸 part。 + +历史交接资料中的 part26 和 part27 仅用于说明素材来源批次,不作为产品运行时的普通贴纸 part。三个区域必须在产品上保持独立,不能把色卡或动态贴纸降级为普通图片。 + +### STATIC-01 普通贴纸范围 + +P0 接入本地后端安全配置所引用的 `%USERPROFILE%\Desktop\贴纸素材` 中全部 1,407 张 PNG。 + +业务规则: + +- 保留原 part1 至 part25; +- 保留所有原始文件; +- part18 与 part25 的 3 组重复图片仍作为两份入口展示和保存; +- 不静默去重; +- 不根据文件名猜测贴纸含义。 + +### STATIC-02 普通贴纸目录 + +由于现有文件名主要为哈希且没有关键词,P0 支持: + +- 按 part 浏览; +- 保持 part 内稳定顺序; +- 账号级最近使用; +- 后台上下架。 + +加载规则: + +- 进入普通贴纸面板时只加载 part 列表和首屏必要数据,不预下载 1,407 张原图; +- 每个 part 使用虚拟列表,只渲染可视区域及少量缓冲项; +- 缩略图按视口懒加载,离开可视范围的列表节点可回收; +- 贴纸原图只在加入画布或导出确有需要时按需加载; +- 最近使用区域同样优先加载缩略图,不因最近使用而批量预取原图。 + +P0 不支持普通贴纸关键词搜索。 + +### STATIC-03 普通贴纸编辑 + +普通贴纸支持: + +- 移动; +- 等比缩放; +- 旋转; +- 水平翻转; +- 0% 至 100% 透明度; +- 复制; +- 删除; +- 层级调整; +- 多选移动。 + +### STATIC-04 后台上传 + +超级管理员可以上传 PNG 或 WebP 普通贴纸,并配置: + +- 稳定素材 ID; +- 所属 part; +- part 内顺序; +- 启用或停用; +- 可选的内部名称。 + +超级管理员上传的普通静态贴纸原图及由 Dada 生成的缩略图写入 LocalDataRoot,并从首次物理写入起计入 managed_content_bytes。产品随安装包提供或通过第 18 节本机安全配置原地引用的只读规范素材库不计入 managed_content_bytes,也不得因后台上传而复制进 LocalDataRoot。 + +storage_status 为 full 或 unavailable 时,后台必须阻止继续上传原图、生成缩略图或写入其他受管二进制内容;已经存在的普通贴纸仍按其权限、启停和版本规则读取。 + +超级管理员可以显式清理不再被任何已发布版本或项目引用的历史上传原图和缩略图。清理前必须展示确认,系统必须再次校验引用关系并形成不可修改的 AdminOperationLog;不得自动清理,不得删除仍被任何已发布版本或项目引用的文件。只有物理清理完成并重新计量后,才能更新 managed_content_bytes、capacity_notice_level、storage_status 和 last_measured_at。 + +P0 后台不能直接上传文字模板、色卡渲染器、Lua、Prefab 或复杂动态资源包。 + +## 13. 色卡贴纸 + +### COL-01 范围 + +完整 P0 接入 COLOR001 至 COLOR016,对应稳定网页样式 style_01 至 style_16。P0-A 只启用第 3.1 节规定的四个色卡 ID。 + +### COL-02 五色提取 + +- 色卡从当前底图未经裁剪、调色和滤镜处理的原始图片提取五个主要颜色; +- 提取在浏览器本地执行; +- 不因色卡功能额外上传底图; +- 提取结果必须可重复,不能在同一图片上随机变化; +- 色卡可以使用额外固定背景色或装饰色,但用户图片调色板为五色。 + +### COL-03 样式映射 + +- style_01 与原生 TYPE_1 有单样本确认; +- style_02 至 style_16 保持当前网页稳定顺序; +- style_02 至 style_16 不宣称与小红书内部 TYPE 编号逐项准确对应; +- 色值只要求接近当前确定性网页实现,不承诺与小红书原生 ColorExtract 模型逐色一致。 + +### COL-04 刷新规则 + +- 首次插入时从当前底图提取五色; +- 用户更换项目底图时,已有色卡自动重新提取; +- 裁剪、调色或滤镜变化不触发重新提取; +- P0 不提供手动吸色或手动刷新按钮; +- 色卡面板标题旁提供信息图标,说明“色卡基于原始底图,更换底图时更新,不随裁剪、调色和滤镜变化”。 + +### COL-05 编辑 + +生成后的色卡作为独立画布元素,支持移动、缩放、旋转、复制、删除和层级调整。 + +## 14. 动态贴纸 + +### DYN-01 范围 + +完整 P0 接入 DYN001 至 DYN035,分类如下: + +| 分类 | 数量 | +| --- | ---: | +| 地点/POI | 6 | +| 时间/日期 | 7 | +| 用户昵称/ID | 21 | +| 综合信息 | 1 | + +P0-A 只启用第 3.1 节规定的十个动态贴纸 ID。 + +原始 inventory 中没有动态绑定的额外目录不计入这 35 个模板。 + +### DYN-02 数据按需提供 + +每个模板只能要求和显示其真实消费的字段。通用 Lua 中存在但当前 Prefab 不消费的相机、日期或位置字段不能变成无关的用户表单。 + +### DYN-03 时间数据 + +- 插入时间贴纸时使用用户浏览器的本地当前日期和时间; +- 用户可以在提交前修改日期和时间; +- 项目保存用户确认后的值; +- 重新打开项目后不自动改成新的当前时间。 + +### DYN-04 地点数据 + +地点贴纸支持: + +- 用户授权浏览器定位; +- 通过部署时配置的地理编码服务转换地点; +- 用户手动修改地点名称; +- 定位拒绝、失败或供应商不可用时回退到手动输入。 + +系统不能读取密码、Cookie、认证令牌或与定位无关的浏览器数据。 + +### DYN-05 坐标保存 + +DYN004 会把经纬度作为可见贴纸内容。 + +当用户选择 DYN004 并授权定位时: + +- 在调用浏览器定位权限前显示一次 Dada 自有提示,明确说明原始经纬度会保存到当前项目并显示在导出成品中; +- 用户确认后才调用浏览器定位,不再要求取得坐标后的第二次确认; +- 保存原始坐标和用户可见格式化值; +- 用户可以手动修改可见文本; +- 坐标只属于当前私有项目; +- 删除项目或注销账号时按对应规则删除。 + +用户拒绝 Dada 自有提示或浏览器定位时,不调用或停止调用定位,也不能自动插入 DYN004。用户可以改用仅显示地点名称的其他模板;其他地点模板默认只保存用户确认的地点文字,不保存原始坐标。 + +### DYN-06 用户身份数据 + +用户类贴纸默认使用注册资料中的: + +- 创作署名; +- 社交 ID。 + +用户可以在单个贴纸中覆盖显示值,覆盖不自动修改账号资料。 + +需要 @ 前缀的模板由模板格式化规则添加,不能重复显示 @@。 + +### DYN-07 静态导出 + +动态贴纸只在编辑器内按数据生成视觉内容。导出 JPG 或 PNG 后: + +- 文字和图形成为静态像素; +- 地点、账号或时间不可点击; +- 不创建小红书平台内的真实 POI、话题或账号链接。 + +### DYN-08 DYN012 字体替代 + +DYN012 引用的 DIN_MediumAlternate.otf 当前缺失。 + +P0 使用字体面板 FONT081,即 Lexend Deca,作为明确替代: + +- 产品和测试记录必须标记为替代字体; +- 不能宣称为原字体; +- 后续获得合法原字体后可通过模板资源升版替换。 + +### DYN-09 运行时边界 + +Android Capa Prefab 和 Lua 仅作为布局、格式化和字段绑定参考。网页产品不得直接执行这些 Android 运行时文件,必须使用转换后的受控模板模型。 + +## 15. 导出需求 + +### EXPORT-01 格式 + +支持: + +- JPG; +- PNG。 + +### EXPORT-02 尺寸 + +导出像素必须与项目画布规格完全一致: + +- 3:4 为 1080 × 1440; +- 1:1 为 1080 × 1080; +- 4:3 为 1440 × 1080; +- 9:16 为 1080 × 1920。 + +### EXPORT-03 质量 + +- 导出使用 sRGB; +- 不添加 Dada 水印; +- PNG 使用无损导出; +- JPG 质量可在 80 至 100 之间选择,默认值为 92; +- 导出结果必须与画布中已提交状态一致; +- 若存在未提交文字或样式修改,必须提示“将应用当前修改并导出”; +- 用户确认后把当前编辑形成一次撤销记录,并等待本地画布渲染稳定后导出; +- 用户取消确认时不提交编辑,也不导出。 + +### EXPORT-04 最新成品 + +每个项目按格式保存最新一份成品: + +- 正常导出由当前客户端根据已提交画布状态完成合成; +- P0-A 客户端将同一成品提供给用户下载并通过本地后端保存到 LocalDataRoot;P0-B 的正式上传方式由后续方案定版; +- 再次导出 JPG 覆盖上一次 JPG; +- 再次导出 PNG 覆盖上一次 PNG; +- 不保存完整导出版本历史; +- 删除项目时一并处理; +- P0-A 可在同一电脑重新下载,P0-B 才支持其他受支持设备再次下载; +- 从未导出过对应格式时,不自动生成成品。 + +没有任何已保存导出成品时,成品区域显示“暂无导出成品”,并提供“进入编辑并导出”和“下载原始生成图”。 + +### EXPORT-05 失败 + +导出失败时: + +- 不覆盖上一份成功成品; +- 显示失败原因和重试入口; +- 不改变项目可编辑状态。 + +### EXPORT-06 后端保存失败时的本地导出 + +当前画布能够在浏览器正确合成、但后端项目保存失败时: + +- 允许用户把当前画面导出并下载到本机; +- 明确提示项目和后端最新成品尚未更新; +- 本地下载不得覆盖后端上一次成功成品; +- 成品上传失败时也不得更新 latest_exports; +- 页面保持打开期间继续重试项目保存; +- 用户刷新、关闭页面或浏览器崩溃后,只恢复最后一次后端 saved 状态,不恢复本地导出时仍未保存的编辑状态。 + +## 16. 运营后台 + +### ADMIN-01 用户管理 + +超级管理员可以: + +- 按邮箱、状态或内部账号标识查询用户; +- 查看注册时间、最近活动、项目数量和生成用量; +- 增加或扣减点数; +- 调点时填写必填原因,并允许把 available_balance 调整为负数; +- 暂停或恢复账号。 + +### ADMIN-02 邀请码 + +超级管理员可以: + +- 创建邀请码; +- 设置最大使用次数; +- 设置有效期; +- 停用邀请码; +- 查看已使用次数。 + +### ADMIN-03 模型管理 + +超级管理员可以配置: + +- 模型标识和展示名; +- 是否启用; +- 是否为默认模型; +- 推荐优先级,使用唯一正整数且 1 为最高,不允许并列; +- 路由类型; +- 支持的比例; +- 参考图数量和文件限制; +- 提示词长度上限; +- 单次点数; +- 超时配置; +- 版本化 error_mapping_profile,用于把供应商错误映射到固定的 GenerationErrorCategory。 + +任何影响模型启停、用户确认或任务结算的配置保存后必须生成新的 config_version,旧版本不能被原地静默改写。 + +后台配置必须始终存在且只存在一个已启用的默认模型。超级管理员主动停用当前默认模型时,必须在同一原子操作中指定另一个已启用模型作为新默认模型;未提供合法替代模型时,整个操作失败,不修改任何模型的启停、默认或推荐优先级。外部服务临时不可用、网关余额不足或其他运行时降级不得改写后台配置的默认模型或推荐优先级。 + +recommendation_priority 缺失、不是正整数或与其他模型重复时,整个模型配置保存操作失败,不允许部分写入。 + +后台只能配置上游错误到现有分类的映射,不能新增、重命名或删除 GenerationErrorCategory。模型端点、请求字段、参考图协议、响应结构、异步模式、任务查询或错误协议变化时,保存配置必须同时把 contract_validation_status 重置为 unverified。 + +后台不能显示完整 API 密钥。 + +### ADMIN-04 生成记录 + +默认列表显示: + +- 用户标识; +- 模型; +- 状态; +- 创建和完成时间; +- 耗时; +- 点数状态; +- 错误类别。 + +图片和完整提示词默认隐藏。 + +### ADMIN-05 私有内容访问审计 + +超级管理员首次进入私有内容查看区域时必须看到一次全局规则提示。提示确认按“super_admin 身份 + 告知版本”持久记录,不按浏览器、标签页或会话记录;同一身份确认同一告知版本后不重复显示,告知文案升版后必须重新提示。图片和完整提示词入口必须显示“打开并记录审计”;点击后不再显示逐次确认,也不要求填写查看原因。每次打开必须自动记录: + +- 管理员账号; +- 被查看的任务或项目; +- 访问时间; +- 查看内容类型。 + +全局提示确认不能替代任何一次 PrivateContentAccessLog。审计记录在 180 天保留期内不可修改或人工删除,到期后由系统清理。只有 super_admin 可以查看审计后台,普通用户不能访问。 + +### ADMIN-06 普通贴纸管理 + +超级管理员可以: + +- 上传 PNG/WebP; +- 选择 part; +- 调整 part 内顺序; +- 启用或下架; +- 查看尺寸和文件状态。 + +下架只阻止新项目添加。已有项目必须继续正确渲染原贴纸,直至项目被删除。 + +后台上传的普通贴纸原图和缩略图计入 managed_content_bytes。storage_status 为 full 或 unavailable 时,后台必须禁用上传、缩略图生成和其他受管二进制写入入口,但仍允许按权限查看已有素材。 + +超级管理员可以在确认后清理不再被任何已发布版本或项目引用的历史上传原图和缩略图。后台必须在执行前重新校验引用关系;仍被引用时拒绝整个清理操作。系统不得自动清理,清理请求、校验结果和最终结果必须写入不可修改的 AdminOperationLog。只有物理清理完成并重新计量后,后台容量汇总和 LocalBackendStorageState 才能更新。 + +### ADMIN-07 复杂模板管理 + +文字、字体、色卡和动态贴纸通过经过校验的版本化资源包发布。后台只支持: + +- 启用或停用; +- 分类顺序; +- 展示顺序; +- 查看当前资源版本和校验状态。 + +后台不提供可视化模板编辑器。 + +复杂模板允许按测试批次导入和验收。超级管理员可以: + +- 创建或关闭内部测试批次; +- 把指定模板标记为 imported、internal_preview、passed、enabled 或 disabled; +- 将指定普通账号授权到指定测试批次并设置有效期; +- 随时撤销预览授权。 + +普通账号获得内部预览授权后,只能在素材面板中看到对应批次,不能访问运营后台、调点、模型配置、邀请码、生成记录或私有内容。白名单外模板即使分批 passed,也必须等全部剩余复杂模板通过后才可在 P0-B 一次性向普通用户 enabled。 + +### ADMIN-08 基础数据 + +后台至少显示: + +- 当前占名额普通账号数,并分别显示 active 与 suspended; +- 历史累计创建普通账号数和已注销普通账号数; +- super_admin 数量,且明确不计入普通账号上限; +- 活跃用户数; +- 各模型生成次数、成功率和失败率; +- 已扣点与已释放点数; +- 项目和图片存储数量; +- P0-A managed_content_bytes(包含后台上传的普通贴纸原图和缩略图)、5 GB 硬上限、capacity_notice_level、storage_status 和素材根目录可用状态,但不向后台回显本机绝对路径; +- 最常使用的文字模板和贴纸 part; +- Resend 当日与当月用量、硬上限、供应商可用状态和暂停原因; +- 高德当月用量、硬上限和暂停原因; +- AI 网关余额可用、不足或未知状态,以及当前识别的模型级、账户级或未知影响范围,但不提供自动充值入口。 + +super_admin 只有在新的免费周期开始或供应商恢复后,才可以恢复被暂停的辅助服务。AI 网关余额不足造成的模型运行时不可用状态必须在真实余额确认恢复后才能解除。后台不能提高 Resend 或高德的 P0 硬上限,也不能开启付费通道。 + +### ADMIN-09 后台操作审计 + +以下敏感操作必须形成不可修改的 AdminOperationLog: + +- 管理员账号首次创建、登录结果、停用、恢复和白名单状态生效; +- 普通用户账号暂停或恢复、点数调整; +- 邀请码创建、停用和限制变更; +- 模型路由、点数、比例、参考图限制、启停、默认模型、推荐优先级和余额恢复; +- 普通贴纸与复杂模板的上传、版本发布、排序、启停和测试批次变更,以及普通贴纸历史原图和缩略图清理的请求、引用校验与结果; +- AssetPreviewGrant 的授予、撤销和到期配置; +- Resend、高德等辅助服务的硬上限下调、暂停和恢复。 + +超级管理员通过后台执行的操作使用 actor_type=super_admin,并以管理员账号标识作为 actor_ref。当前阶段后端安全配置中的管理员白名单、身份冲突处理或恢复配置被 Dada 读取并生效时使用 actor_type=system,actor_ref 固定为 backend_secure_config;不得记录执行配置变更的人员身份、白名单内容、密钥或其他安全配置值。 + +私有图片或完整提示词查看继续写入独立的 PrivateContentAccessLog,不要求在 AdminOperationLog 中重复保存私有内容或目标正文。两类日志均不得记录验证码、API 密钥、会话令牌、完整提示词、图片内容或其他不必要的私有正文;除系统按保留期清理外,任何管理员和后台功能均不能修改或删除记录。 + +## 17. 产品数据契约 + +本节定义产品语义,不指定数据库表、API JSON 或代码类型。 + +### 17.1 UserProfile + +| 字段 | 含义 | +| --- | --- | +| user_id | Dada 内部不可猜测标识 | +| email | 登录邮箱 | +| creator_name | 创作署名 | +| social_id | 成品展示用社交 ID | +| role | user 或 super_admin | +| account_status | active、suspended 或 deleted | +| counts_toward_stage_limit | 普通账号为 true,super_admin 为 false | +| available_balance | 可用于新任务的点数,超级管理员调点时可为负数 | +| reserved_balance | 运行中任务冻结点数,不能被管理员直接修改 | +| private_content_notice_version | super_admin 最近确认的私有内容区域告知版本;普通用户为空 | +| private_content_notice_acknowledged_at | super_admin 确认该告知版本的时间;普通用户为空 | +| created_at | 注册时间 | + +### 17.2 ModelConfig + +| 字段 | 含义 | +| --- | --- | +| model_id | 上游模型标识 | +| display_name | 前台名称 | +| config_version | 每次影响用户确认或任务结算的配置变更均递增 | +| route_profile | 网关路由和协议配置 | +| gateway_account_ref | 不包含密钥的网关结算账户引用,用于判断共享账户的模型停用范围 | +| error_mapping_profile | 版本化供应商错误映射,只能指向固定 GenerationErrorCategory | +| enabled | 是否允许新任务 | +| is_default | 是否为后台配置默认模型;所有配置中必须始终恰好有一个已启用默认模型 | +| recommendation_priority | 运行时推荐顺序使用的唯一正整数;1 为最高优先级,不允许模型间并列 | +| credit_cost | 单次点数 | +| supported_ratios | 支持比例 | +| reference_limits | 参考图数量、格式和大小限制 | +| prompt_limit | 提示词长度限制 | +| safety_source | 当前依赖的供应商安全策略 | +| contract_validation_status | unverified、verified 或 blocked;契约相关配置变化后立即重置为 unverified | +| contract_evidence_ref | 三模型网关脱敏验证证据引用 | + +### 17.3 Project + +| 字段 | 含义 | +| --- | --- | +| project_id | 私有项目标识 | +| owner_id | 项目所有者 | +| name | 可修改项目名 | +| canvas_ratio | 创建后不可修改的画布比例 | +| canvas_size | 固定导出像素 | +| active_background_id | 当前生成历史底图 | +| background_adjustments | 裁剪、适配和调色状态 | +| canvas_elements | 文字和贴纸元素,最多 50 个 | +| latest_exports | 最新 JPG 和 PNG | +| state_version | 自动保存和冲突检测版本 | +| save_status | saved、dirty、saving、failed 或 conflicted | +| status | active、trashed 或 purged;只有 active 计入 20 个活动项目上限 | +| draft_state | generating、failed_empty 或 has_success_output | +| batch_deletable | 仅没有成功生成图的 failed_empty 草稿为 true | +| deleted_at / purge_at | 进入回收站时间,以及精确增加 720 小时后的不可恢复时间 | +| created_at / updated_at | 创建与更新时间 | + +### 17.4 GenerationJob + +| 字段 | 含义 | +| --- | --- | +| generation_id | 任务标识 | +| project_id | 所属项目 | +| model_id | 实际模型 | +| model_config_version | 用户确认并提交时使用的配置版本 | +| confirmed_credit_cost | 用户提交前确认的点数成本 | +| prompt | 用户提示词 | +| ratio | 请求比例 | +| reference_assets | 私有参考图关系 | +| status | queued、running、succeeded、failed、rejected | +| reserved_credits | 冻结点数 | +| final_credit_state | 扣除或释放 | +| output_asset | 成功图片 | +| error_category | 引用 GenerationErrorCategory;failed 或 rejected 时必填,queued、running 或 succeeded 时为空 | +| created_at / finished_at | 任务时间 | + +### 17.5 CanvasElement + +共有字段: + +- element_id; +- type; +- template_or_asset_id; +- position; +- scale; +- rotation; +- z_index; +- opacity; +- created_at。 + +type 取值: + +- text_template; +- static_sticker; +- color_card; +- dynamic_sticker。 + +文字元素额外保存内容、模板 ID、字体覆盖、字号和样式参数。色卡保存当前五色结果和 style ID。动态贴纸保存模板真实消费的字段、格式化值,以及 DYN004 经授权的原始坐标。 + +### 17.6 TemplateRegistry + +复杂模板注册项至少包含: + +- 稳定 ID; +- family; +- category; +- display_name; +- default_text; +- resource_class; +- resource_version; +- canonical resource reference; +- default font; +- preview reference; +- required dynamic fields; +- release_status; +- test_batch_ids; +- display order; +- validation status。 + +release_status 取值为 imported、internal_preview、passed、enabled 或 disabled。复杂模板额外包含 release_tier,取值为 alpha_whitelist 或 full_p0。 + +- P0-A 普通用户只可见 alpha_whitelist、validation status 为 passed 且 release_status 为 enabled 的模板; +- 指定内部测试账号可见授权批次中 release_status 为 internal_preview 或 passed 的模板; +- full_p0 模板可以分批进入 passed,但必须在全部剩余复杂模板通过后一次性改为 enabled; +- disabled 对所有普通账号和内部测试账号隐藏,但已有项目仍按其资源版本渲染。 + +### 17.7 StaticStickerCatalog + +普通贴纸目录至少包含: + +- 稳定贴纸 ID; +- part 编号; +- part 内顺序; +- 原始文件名; +- 相对资源路径; +- thumbnail reference; +- 文件格式; +- 宽高; +- 文件哈希; +- enabled; +- resource version; +- asset_origin:bundled_read_only 或 admin_uploaded。 + +普通贴纸目录项属于 public_release_asset;目录和缩略图可以公开只读,但不能由公开目录推导用户私有资源地址。 + +asset_origin 为 admin_uploaded 时,原图和由 Dada 生成的缩略图存放在 LocalDataRoot 并计入 managed_content_bytes;asset_origin 为 bundled_read_only 时,资源属于产品随安装包提供或通过第 18 节安全配置原地引用的只读规范素材库,不计入 managed_content_bytes。历史上传原图和缩略图只有在不再被任何已发布版本或项目引用时才可显式清理。 + +重复文件仍使用不同贴纸 ID 和目录条目。 + +### 17.8 DynamicStickerData + +动态数据提供器按模板需要提供以下受控字段: + +- 时间和日期; +- 地点名称、城市或英文城市名; +- 经授权的经纬度; +- 创作署名; +- 社交 ID; +- 模板明确消费的格式化派生值。 + +不得因为通用脚本包含字段,就要求用户填写当前模板不使用的信息。 + +### 17.9 CreditAccount 与 CreditLedger + +CreditAccount 至少包含 available_balance、reserved_balance 和更新时间。每次点数变化必须形成不可静默修改的流水,流水至少包含类型、数量、操作前后两类余额、关联任务或管理员、管理员调点原因和时间。 + +流水类型支持: + +- registration_grant; +- generation_reserve; +- generation_commit; +- generation_release; +- admin_adjustment。 + +### 17.10 PrivateContentAccessLog + +记录超级管理员查看私有内容的事件,至少包含管理员账号、目标、访问时间、查看内容类型和 expires_at。expires_at 固定为访问事件发生后 180 天。记录在保留期内不可修改或人工删除,到期后由系统清理。 + +用户注销后,管理员账号继续保留,用户和目标替换为不可反推的随机匿名标识;注销不会延长原日志的 expires_at。 + +### 17.11 AnonymousRetainedEvent + +注销后保留的匿名事件明细至少定义: + +- anonymous_subject_id:注销时生成且不保留身份映射的随机标识; +- event_type; +- model_id; +- outcome 和 error_category; +- credit_delta; +- occurred_at; +- expires_at:不晚于注销时间后 180 天。 + +该对象不得包含邮箱、IP、提示词、图片、项目 ID、原任务 ID 或其他可恢复内容。 + +### 17.12 AssetReleaseManifest + +版本化素材发布清单至少包含: + +- release_version; +- storage_backend; +- storage_base_ref; +- 资源稳定 ID、相对路径、大小和哈希; +- access_class:public_release_asset、internal_preview_asset 或 private_user_asset; +- catalog 与 metadata 版本; +- release_tier; +- validation_status; +- created_at。 + +AssetReleaseManifest 是后端内部资源清单,可以用 access_class 区分资源。浏览器可读取的公开 manifest 是其受控导出,只能包含 public_release_asset。P0-A 的 storage_backend 固定为 local_filesystem,storage_base_ref 只引用本地安全配置中的素材根目录,不向浏览器暴露本机绝对路径。internal_preview_asset 不得进入公开 manifest,其资源路径不得仅由模板稳定 ID 推导;授权账号只能通过本地后端按 AssetPreviewGrant 下发的授权 manifest 和登录鉴权资源接口访问。private_user_asset 的路径和可推导标识同样不得进入公开导出,必须通过所有者登录鉴权或经审计的超级管理员访问。P0-A 运行时依赖发布清单和本机只读素材根目录,不依赖公司对象存储;P0-B 的 storage_backend 和 storage_base_ref 必须在公司系统合并时重新定版,且不得在本版预设为 COS。 + +### 17.13 AssetPreviewGrant + +内部素材预览授权至少包含: + +- grant_id; +- user_id:被授权的普通账号; +- test_batch_id; +- granted_by:执行授权的 super_admin; +- granted_at / expires_at; +- status:active、revoked 或 expired。 + +AssetPreviewGrant 不改变 UserProfile.role,不进入公开素材 manifest,也不能授予批次预览以外的任何权限。P0-A 本地后端每次下发内部预览 manifest 或读取内部预览资源前必须重新校验登录状态和授权;授权被撤销、到期或账号被停用后不得再取得资源。P0-B 若采用短时地址,必须在公司系统合并方案中重新定义有效期和撤销语义。 + +### 17.14 ClientCachePolicy + +客户端主动持久缓存策略至少包含: + +- max_public_cache_bytes:157,286,400 字节,对用户显示为 150 MB 上限; +- current_public_cache_bytes; +- eviction_policy:LRU; +- cached_release_version; +- last_eviction_at。 + +只有 public_release_asset 中的公开缩略图、公开模板转换产物和公开字体可以进入该缓存。internal_preview_asset、提示词、私有图片引用、项目状态、未保存编辑内容、验证码和会话令牌不得进入该缓存。用户主动下载到系统下载目录的图片不属于 Dada 缓存。 + +### 17.15 ExternalServiceUsage + +免费辅助服务用量至少包含: + +- service_id:resend_email 或 amap_web_service; +- period_type 与 period_start; +- hard_limit; +- used_count; +- service_status:active、paused_quota、paused_provider 或 disabled; +- pause_reason; +- updated_at。 + +service_status 只使用上述四种状态,不设置独立的运行时邮件质量降级状态。达到 hard_limit 后必须先把服务状态改为 paused_quota,再拒绝后续调用;Resend 接口不可用、明确退信故障或其他供应商异常统一使用 paused_provider,并通过 pause_reason 记录非敏感错误类别。发布前人工送达测试失败只阻止发布,不写入运行时 service_status。P0 不设计支付、自动购买、自动充值或自动扩容状态。 + +### 17.16 AdminOperationLog + +后台敏感操作审计至少包含: + +- log_id; +- actor_type:super_admin 或 system; +- actor_ref:super_admin 使用管理员账号标识,system 使用 backend_secure_config 等固定非个人来源标识; +- operation_type; +- target_type 和不可猜测的 target_ref; +- result:succeeded 或 failed; +- before_summary / after_summary:仅保存完成审计所需的非敏感状态摘要; +- occurred_at; +- expires_at:固定为 occurred_at 后 180 天。 + +记录在保留期内不可修改或人工删除,到期后由系统清理。不得记录验证码、API 密钥、会话令牌、完整提示词、图片内容或其他不必要的私有正文。私有内容查看使用 PrivateContentAccessLog,不在本对象中重复保存。 + +### 17.17 GatewayBalanceState + +AI 网关余额状态至少包含: + +- gateway_account_ref:不包含密钥的网关结算账户配置引用; +- balance_status:available、insufficient 或 unknown; +- impact_scope:model、account 或 unknown; +- affected_model_ids; +- runtime_unavailable_model_ids; +- detected_at 和 last_confirmed_at; +- recovery_status:not_required、awaiting_confirmation 或 confirmed; +- source_category:用于说明来自网关响应、后台配置或人工确认的非敏感类别。 + +impact_scope 为 model 时仅把明确受影响模型标记为运行时不可用;为 account 或 unknown 时把所有共享 gateway_account_ref 的模型标记为运行时不可用。该状态不得改写 ModelConfig.enabled、is_default 或 recommendation_priority。恢复运行时可用状态前必须把余额恢复确认为 confirmed,并形成 AdminOperationLog。 + +### 17.18 GenerationErrorCategory + +生成提交响应和失败任务使用同一套固定产品错误注册表。每个分类至少定义: + +- category_id:GEN-16 固定的九个 error_category 之一; +- task_outcome:not_created、failed、rejected 或 failed_or_not_created; +- user_action:retry_same_input、wait_and_retry、edit_input、choose_model_or_contact_admin 或 contact_admin; +- retry_policy:immediate、after_wait、after_edit、after_model_change 或 none; +- credit_behavior:no_reserve 或 release_if_reserved; +- message_key:供 UIDesign.md 绑定用户文案的稳定键。 + +该注册表在 P0 中不是后台可编辑素材。ModelConfig.error_mapping_profile 只能把上游结果映射到现有 category_id;不得把供应商原始错误码、错误正文、堆栈或内部地址作为用户消息。提交前错误返回分类但不创建 GenerationJob;任务创建后的 failed 或 rejected 必须记录分类并只释放一次冻结点数。 + +### 17.19 LocalBackendStorageState + +P0-A 本机内容存储状态至少包含: + +| 字段 | 含义 | +| --- | --- | +| storage_backend | 固定为 local_filesystem | +| data_root_ref | LocalDataRoot 的安全配置引用,不保存硬编码用户名或对前端暴露绝对路径 | +| managed_content_bytes | Dada 管理的参考图、生成图、成品、派生二进制文件,以及后台上传的普通贴纸原图和缩略图当前总字节数 | +| hard_limit_bytes | 固定为 5,368,709,120 字节,对用户显示为 5 GB | +| capacity_notice_level | normal、warning 或 critical;按 managed_content_bytes 与硬上限比例确定 | +| storage_status | active、full 或 unavailable | +| last_measured_at | 最近一次容量测量时间 | + +LocalDataRoot 默认位于当前 Windows 用户的应用数据目录,必须可配置,且不得放在 Git 仓库或项目目录中。P0-A 的本机数据库、账号、项目、参考图、生成图、导出成品、审计日志和派生文件均写入 LocalDataRoot。 + +LocalDataRoot 是本地后端管理的文件系统存储,不是浏览器 localStorage、IndexedDB 或 Cache Storage。后端把已提交项目状态写入本机磁盘,不改变“浏览器不得持久化私有草稿、提示词或图片引用”的规则。 + +managed_content_bytes 只统计 Dada 管理的参考图、生成图、导出成品、派生二进制文件,以及 super_admin 通过后台上传的普通静态贴纸原图和由 Dada 生成的缩略图。产品随安装包提供或通过第 18 节本机安全配置原地引用的只读规范素材库、应用代码、157,286,400 字节的浏览器公开素材缓存、数据库元数据、审计记录和用户主动下载目录均不计入。5 GB 是整个 P0-A 本机实例的内容硬上限,不按账号重复计算。 + +数据库元数据、审计记录、诊断日志和错误日志不计入 5 GB 内容额度,也不在本版增加单独文件大小硬上限。DevelopmentPlan 必须定义 180 天审计清理的执行机制、诊断与错误日志轮转,以及数据库或日志目录不可写时把 storage_status 切换为 unavailable 的处理,防止这些文件无限增长或写入失败后继续调用 AI。 + +capacity_notice_level 按以下固定阈值计算: + +- normal:managed_content_bytes 低于 4,294,967,296 字节,即低于硬上限的 80%; +- warning:managed_content_bytes 达到 4,294,967,296 字节且低于 4,831,838,208 字节,即达到 80% 且低于 90%;显示非阻断容量提醒; +- critical:managed_content_bytes 达到 4,831,838,208 字节且低于 5,368,709,120 字节,即达到 90% 且尚未满额;持续显示清理提醒。 + +warning 和 critical 均不阻止生图、参考图持久化、最新成品保存、后台普通贴纸原图上传或缩略图生成,也不得触发自动删除。critical 提示必须按以下顺序引导用户:先下载需要保留的成品,再永久删除不需要的项目,并可由 super_admin 确认清理符合条件的无引用历史贴纸文件,随后等待物理清理和容量重新测量,storage_status 恢复 active 后继续生成。告警展示、容量监控和物理清理队列的工程实现由 DevelopmentPlan 定义,但不得改变阈值、阻断条件或人工清理顺序。 + +- 达到或预计本次写入后超过 hard_limit_bytes 时,storage_status 为 full;系统不得自动删除任何文件,必须阻止新生图、参考图持久化、latest_exports 保存、后台普通贴纸原图上传、缩略图生成和其他受管二进制写入,并且不得创建 AI 任务、调用上游或冻结点数; +- full 状态仍允许编辑已有项目、按既有权限查看和下载已有文件、永久删除项目释放空间、显式清理符合条件的历史普通贴纸原图和缩略图,以及只下载但不更新 latest_exports 的客户端导出; +- LocalDataRoot 不可写或系统磁盘已满时,storage_status 为 unavailable;系统停止新增写操作、后台普通贴纸原图上传、缩略图生成和 AI 调用,但保留已有项目及素材的查看、下载和删除入口; +- 普通贴纸历史文件不得自动清理。只有不再被任何已发布版本或项目引用的后台上传原图和缩略图才可由 super_admin 确认后显式清理;清理必须形成不可修改的 AdminOperationLog,仍有引用时必须拒绝; +- 对项目文件或普通贴纸历史文件执行清理后,只有物理清理完成并重新计量,系统才可更新 managed_content_bytes、capacity_notice_level、storage_status 和 last_measured_at;LocalDataRoot 可写且容量低于硬上限时才能恢复 active; +- P0-A 不创建自动备份,本机测试账号、项目、内容和日志均视为可丢失,不自动或手动迁移到公司正式系统;需要保留的原始生成图或 JPG/PNG 成品由用户主动下载; +- P0-A 不对本机文件实施 Dada 应用层加密,依赖当前 Windows 用户登录和文件系统权限保护。 + +## 18. 素材来源与运行边界 + +### 18.1 总交接入口 + +以下路径表示 2026-07-23 当前机器上的素材交接默认位置,使用 Windows `%USERPROFILE%` 占位符,不包含具体用户名。P0-A 通过本地后端安全配置解析并引用这些素材根目录,原地只读复用;解析后的绝对路径不得写入 PRD、Git、普通项目数据或浏览器响应: + +- 交接说明:%USERPROFILE%\Desktop\sticker_web_handoff\STICKER_WEB_REPLICATION_HANDOFF.md +- 机器总入口:%USERPROFILE%\Desktop\sticker_web_handoff\sticker_web_catalog_manifest.json +- 完整性报告:%USERPROFILE%\Desktop\sticker_web_handoff\sticker_archive_validation_20260722.json + +当前完整性报告 all_valid 为 true。 + +### 18.2 文字模板 + +- 根目录:%USERPROFILE%\Desktop\sticker_text\模板\单模板归档 +- 规范入口:各分类 catalog.csv 和 templates//metadata.json + +### 18.3 字体 + +- 根目录:%USERPROFILE%\Desktop\sticker_text\字体\面板全量采集\font_panel_full_20260722 +- 规范入口:reports/font_panel_catalog.csv 和单字体 metadata.json + +### 18.4 文字行为资料 + +- 输入资料:%USERPROFILE%\Desktop\sticker_text\键盘 +- 样式资料:%USERPROFILE%\Desktop\sticker_text\样式 + +### 18.5 普通贴纸 + +- 根目录:%USERPROFILE%\Desktop\贴纸素材 +- 当前结构:sticker_part1 至 sticker_part25 +- 当前数量:1,407 张 PNG + +普通贴纸当前缺少统一 catalog,开发准备阶段必须生成 StaticStickerCatalog,但不得改写原始素材。 + +### 18.6 色卡 + +- 根目录:%USERPROFILE%\Desktop\sticker_colour\单模板归档 +- 规范入口:catalog.csv 和 styles//metadata.json + +### 18.7 动态贴纸 + +- 根目录:%USERPROFILE%\Desktop\sticker_interactive\单模板归档 +- 规范入口:catalog.csv 和 templates//metadata.json + +### 18.8 只读规则 + +- 规范归档是运行素材转换的唯一输入; +- 历史 PB、截图、录像、采集批次和对账目录仅为证据层; +- 产品运行时不读取证据层; +- 不删除、覆盖或原地改写历史资源; +- 规范目录中的硬链接资源按只读处理; +- 需要修正模板时通过新资源版本发布。 + +### 18.9 P0-A 本机素材交付 + +- 现有约 3.57 GB 规范素材通过第 18.1 节的 manifest 和本节各素材根目录原地只读复用,不复制全库,不提交到 Git,也不放入 LocalDataRoot; +- Git 只保存版本化 AssetReleaseManifest、catalog、metadata、版本号和哈希; +- 素材根目录由本地后端安全配置管理,代码和普通项目数据不得硬编码当前用户名或桌面绝对路径; +- 浏览器只能使用资源 ID 调用本地后端接口,不得通过 file://、本机绝对路径或 File System Access API 直接读取素材根目录;本地后端响应不得回显素材根目录绝对路径; +- 目录移动、改名、不可读或清单校验失败时,相关素材功能进入 unavailable 并提示重新配置路径,不修改代码、不自动复制素材; +- 本地后端根据 manifest 提供 public_release_asset 的公开只读接口,并对 internal_preview_asset 执行登录状态和 AssetPreviewGrant 校验; +- 发布新资源版本时创建新的 manifest 与版本引用,不覆盖已经被项目引用的旧版本; +- 产品随安装包提供或由本机安全配置原地引用的只读规范素材库不计入 managed_content_bytes;super_admin 后台上传的普通贴纸原图和缩略图必须存放在 LocalDataRoot 并计入 managed_content_bytes,不得写回只读素材根目录; +- 开发准备产生的临时转换文件必须计入 LocalBackendStorageState 的派生二进制文件,确认不再使用后可以显式清理,但不得改写素材源目录; +- P0-B 的正式存储、制品发布、CI 获取和资源交付格式由公司系统合并方案重新定版,本版不假设使用 COS 或任何特定对象存储。 + +### 18.10 P0-A 本机数据目录 + +- LocalDataRoot 与只读素材根目录严格分离; +- 默认位置使用当前 Windows 用户的应用数据目录,由本地后端解析,不硬编码用户名; +- 本机数据库、账号、项目、参考图、生成图、导出成品、审计日志和派生文件写入 LocalDataRoot; +- LocalDataRoot 不得位于 Git 仓库、项目目录或用户主动下载目录; +- 浏览器只能使用资源 ID 通过本地后端鉴权接口访问 LocalDataRoot 中的私有文件,不得获得绝对路径,也不得通过 file:// 或 File System Access API 直接读取;用户主动使用标准文件选择器选择参考图不受此限制; +- 删除项目、回收站到期和账号注销均按本 PRD 规则删除相关本机文件;用户侧不可恢复后,物理文件可以由本地后端异步清理; +- 删除 LocalDataRoot 或重新安装时不提供自动恢复;P0-A 不创建自动备份,也不向 P0-B 自动或手动导入测试账号、项目或日志; +- P0-A 不提供可编辑项目包、数据库、账号、点数、审计日志或素材关系的导出与导入。用户只能主动下载原始生成图和 JPG/PNG 成品,且这些图片不作为 P0-B 正式系统的数据迁移来源。 + +## 19. 隐私、安全与合规要求 + +### PRIV-01 API 密钥 + +- P0-A API 密钥只能存在于本地后端安全配置;P0-B 迁移到公司正式安全配置; +- 不进入前端包; +- 不进入 PRD、日志、项目或错误提示; +- 后台不能回显完整密钥。 + +### PRIV-02 用户内容 + +- 提示词、参考图、生成图和项目状态均为私有内容; +- 发往 AI 网关、Resend 和高德的外部传输必须使用 HTTPS;P0-A 浏览器与 localhost 本地后端之间不作为远程公开传输; +- 存储访问必须按项目所有者鉴权; +- P0-A 私有资源只能通过本地后端登录鉴权接口访问,不能进入公开素材 manifest 或本地公开静态目录; +- 浏览器不得通过 file://、本机绝对路径或 File System Access API 直接读取素材根目录、LocalDataRoot 或私有文件;本地后端接口使用资源 ID 且不得回显绝对路径;用户主动使用标准文件选择器选择参考图不受影响; +- 未保存画布、提示词、私有图片引用和编辑内容不得写入 localStorage、IndexedDB 或 Cache Storage; +- 超级管理员查看内容必须自动审计;记录在 180 天保留期内不可修改或人工删除,到期后由系统清理。 + +### PRIV-03 定位 + +- DYN004 请求浏览器定位前必须说明坐标会保存到项目并显示在导出成品中; +- 用户拒绝后仍可手动输入地点; +- 用户拒绝 Dada 提示后不得调用浏览器定位或自动插入 DYN004; +- 只有 DYN004 在一次明确提示并取得同意后保存原始坐标; +- 坐标不用于广告、用户画像或公开搜索。 + +### PRIV-04 第三方处理 + +简短《内测使用与隐私告知》必须告知用户: + +- 提示词、参考图和必要参数会发送到所选图片模型服务; +- 邮箱验证码由 Resend 处理; +- 定位可能发送到个人认证的高德 Web 服务; +- P0-A 图片、字体和模板素材从项目方电脑上的只读素材根目录经本地后端提供,账号、项目和生成内容保存在 LocalDataRoot;P0-B 的正式存储在公司系统合并时重新告知; +- P0-A 测试数据依赖当前 Windows 用户登录和文件系统权限保护,Dada 不提供应用层加密或云备份;机器损坏、重装或 LocalDataRoot 被删除后数据不可恢复,重要原始生成图和 JPG/PNG 成品需要用户主动下载; +- 超级管理员在排障或管理场景下可以直接查看私有内容且操作会被记录; +- 注销时内容立即删除,匿名事件和访问审计最多保留 180 天。 + +P0-A 本地测试页或设置区域还必须明确告知:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +### PRIV-05 素材授权 + +项目方已确认当前素材获得公开使用授权。授权记录整理暂不作为 P0-A 或 P0-B 阻塞门槛;正式公开注册或商业化前必须保留可核查记录,并明确字体、贴纸和模板的来源、商用、再分发和二次创作导出范围。该假设不构成对法律风险的绝对保证。 + +### PRIV-06 内测告知版本 + +- 注册必须勾选同意简短《内测使用与隐私告知》; +- 告知必须具有版本号和生效日期,账号记录用户同意的版本与时间; +- P0 不要求单独生成完整法律条款,但简短告知不得省略 PRIV-04 的处理方和数据用途; +- 正式公开注册前必须升级并重新评估完整服务条款与隐私政策。 + +## 20. 非功能需求 + +### NFR-01 支持环境 + +P0-A 只正式支持运行本地后端的同一台 Windows 电脑上的当前稳定版 Chrome 和 Edge,且只能通过 localhost 访问。P0-A 不支持 macOS Safari、iOS Safari、Android Chrome、手机、局域网、远程访问或跨设备流程。 + +P0-A 必须在进入任何产品功能前硬性检查浏览器支持状态。非当前支持版本的 Chrome/Edge 以及其他浏览器只能进入浏览器不受支持引导页,不得继续进入登录、注册、项目、编辑器或运营后台;引导页不得提供绕过或“仍然继续”入口。 + +P0-B 在完成公司系统合并与远程部署验收后,计划支持发布时当前稳定版的 Windows Chrome、Windows Edge、macOS Safari、iOS Safari 和 Android Chrome,其中手机竖屏与横屏提供完整编辑。各阶段均不承诺兼容验收时的前一稳定版或更旧版本。 + +### NFR-02 视觉一致性 + +同一发布阶段中提供相同编辑能力的支持环境必须保持: + +- 相同字体选择; +- 相同换行; +- 相同布局; +- 相同颜色、描边、背景和元素尺寸; +- 相同导出像素。 + +阶段范围按以下规则执行: + +- P0-A 只对核心白名单文字模板及其字体、4 个色卡和 10 个动态贴纸,在本机 Windows Chrome 与 Edge 当前稳定版之间执行视觉一致性验收,对应 AC-32; +- P0-B 才对全部 332 个文字模板、86 个字体项、16 个色卡和 35 个动态贴纸,在 Windows Chrome/Edge、macOS Safari、iOS Safari 和 Android Chrome 当前稳定版中执行全量视觉回归,对应 AC-26; +- P0-B 的全量和跨平台回归结果不属于 P0-A 完成条件。 + +视觉回归使用固定浏览器版本、操作系统、视口、DPR、字体加载完成状态、动画状态和确定动态数据生成基准图。 + +- 元素边界相对基准图偏差不得超过 2px; +- 任一颜色通道绝对差异超过 16/255 的像素视为显著差异; +- 显著差异像素不得超过画布总像素的 1%; +- 超过任一阈值必须人工复核,不能自动判定通过; +- DYN012、style_02 至 style_16 和其他已知替代或近似项无论是否低于阈值均需人工复核。 + +### NFR-03 编辑性能 + +P0-A 电脑完整编辑器和 P0-B 电脑完整编辑器在包含 50 个元素的标准画布中: + +- 选择和移动元素不能出现持续无响应; +- 画布控件不能因动态内容改变尺寸而跳动; +- 自动保存不能阻塞正在进行的本地画布操作; +- 导出失败不能破坏项目状态。 + +P0-B 手机完整编辑器采用两级门槛: + +- 20 个混合元素为常规画布,选择、移动、缩放、旋转、撤销重做和面板操作不得出现持续无响应或操作丢失; +- 50 个混合元素为压力上限,必须能完成选择、修改、保存和导出; +- 50 元素压力模式允许关闭实时辅助线或降低编辑预览分辨率,但必须明确提示,不得删除元素、改变布局或降低最终导出分辨率与质量; +- 手机旋转屏幕不得改变画布内容、选区、撤销栈或未保存状态。 + +具体性能预算由 DevelopmentPlan 定义。 + +### NFR-04 内测容量 + +P0-A 人工验收固定使用项目方控制的 1 个超级管理员账号和 1 个普通账号,不要求朋友参与或招满 10 人。跨普通用户隔离验收由测试程序在同一电脑的独立浏览器配置中临时创建第二个普通账号,按普通权限和名额规则运行,完成后清理。系统最多开放 10 个普通账号,P0-B 最多开放 50 个普通账号。active 和 suspended 普通账号占名额,deleted 普通账号释放名额,super_admin 不占普通账号名额。同账号仅一个运行中生成任务,系统必须具备用量、失败和本机存储状态监控。 + +### NFR-05 数据完整性 + +- 点数扣减必须原子化; +- 只有带完整流水的超级管理员调点可以使 available_balance 为负数,生成与结算流程不得制造负数; +- 自动保存必须有明确成功或失败状态; +- 项目资源与项目状态必须保持引用一致; +- 素材下架不能破坏已有项目; +- 账号注销不能残留可恢复作品; +- AdminOperationLog 和 PrivateContentAccessLog 在 180 天保留期内不得被后台修改或人工删除,到期后必须由系统清理。 + +### NFR-06 可访问性基础 + +- 关键按钮必须可用键盘访问; +- 控件不能只依赖颜色表达状态; +- 图片上传、生成状态和错误必须有文字说明; +- 图标名称和焦点顺序由 UIDesign.md 明确。 + +### NFR-07 客户端存储上限 + +- Dada 主动建立的公开素材持久缓存硬上限为 157,286,400 字节,对用户显示为 150 MB; +- 缓存采用 LRU 淘汰,资源升版后优先清理不再引用的旧版本; +- 不预下载全量文字模板、普通贴纸、色卡、动态贴纸或字体; +- 用户主动下载到系统下载目录的成品不计入 Dada 缓存; +- 浏览器自身不受 Dada 控制的通用 HTTP 缓存不计入应用内计数,但 Dada 不能借此建立第二套应用持久缓存。 + +### NFR-08 辅助服务零新增自动费用 + +- P0-A 使用项目方现有电脑、本机磁盘和用户主动配置的 AI 生图额度,不接入会自动转为付费的辅助服务; +- 邮件和高德达到应用侧硬上限时必须暂停对应调用,不能购买流量、自动充值或自动扩容; +- AI 网关不得开启自动充值;模型级余额不足只把对应模型标记为运行时不可用,账户级或无法确定影响范围时把共享该网关结算账户的全部模型标记为运行时不可用,不改写后台默认模型; +- P0 不承诺 AI 调用成本为零;P0-B 公司基础设施费用需在合并方案中另行确认。 + +### NFR-09 P0-A 本机运行边界 + +- 本地后端只监听 localhost 回环地址,不监听局域网地址,也不提供公网映射; +- LocalDataRoot 必须位于项目目录和 Git 仓库之外,并使用当前 Windows 用户权限读写; +- P0-A 不创建本机自动备份,不承诺恢复被删除的数据目录或操作系统故障后的测试数据; +- 既有约 3.57 GB 素材原地只读使用,不复制到 LocalDataRoot; +- API 密钥只进入本地后端安全配置,不写入仓库、前端、manifest 或本机数据日志。 +- DevelopmentPlan 必须定义审计清理、诊断与错误日志轮转,以及数据库或日志目录不可写时进入 unavailable 的实现;本版不规定数据库或日志文件大小硬上限。 + +## 21. 异常与边界状态 + +| 场景 | 预期产品行为 | +| --- | --- | +| 邀请码无效或过期 | 阻止注册并说明原因 | +| 已注册邮箱再次注册 | 不重复创建账号,明确引导登录 | +| 未注册或已注销邮箱尝试登录 | 不创建会话,明确引导使用邀请码注册 | +| 暂停账号注册或登录 | 不创建账号或会话,明确提示联系管理员 | +| 非管理员白名单邮箱尝试后台登录 | 发送验证码前拒绝,不创建管理员账号或会话 | +| 管理员被停用或移出白名单 | 已有管理员会话立即失效,不允许继续后台操作 | +| 全部管理员被停用 | 后台不提供自助恢复,只能由部署人员通过当前阶段的后端安全配置恢复 | +| 验证码错误或过期 | 不创建会话,允许按频率限制重试 | +| 点数不足 | 不创建任务,不调用上游 | +| 超级管理员扣点超过可用余额 | available_balance 可变为负数,reserved_balance 不变并记录完整流水 | +| 模型被停用 | 从新任务选择中移除,历史仍可见 | +| 超级管理员停用当前默认模型但未提供合法替代模型 | 原子操作整体失败,原启停、默认和推荐优先级均不变 | +| 模型推荐优先级缺失、非正整数或重复 | 整个模型配置保存失败,不允许部分写入 | +| 后台默认模型运行时不可用 | 不改写配置默认模型;在其他运行时可用模型中按 recommendation_priority 返回推荐模型,没有可用模型时返回空推荐并进入既有生成禁用或失败状态 | +| 当前比例不被模型支持 | 要求重新选择比例 | +| 参考图数量超出模型限制 | 阻止提交并指出超出数量 | +| 提交时 ModelConfig 已变化 | 不创建任务或冻结点数,显示最新配置并要求重新确认 | +| 已有运行中任务 | 返回当前任务,不创建第二个 | +| upstream_timeout、upstream_failed 或 unknown_retryable | 进入 failed,释放点数,保留草稿并按分类提供重试 | +| AI 网关明确为模型级余额不足 | 释放任务冻结点数,只把明确受影响模型标记为运行时不可用,不改写后台默认模型 | +| AI 网关账户级余额不足或影响范围未知 | 释放任务冻结点数,把共享该网关结算账户的全部模型标记为运行时不可用,不改写后台默认模型 | +| safety_rejected | 进入 rejected,不保存输出,释放点数并要求修改输入 | +| 生成历史达到 10 张 | 要求删除旧图后再生成 | +| active 项目达到 20 个 | 阻止创建或恢复;trashed 项目保持原 purge_at,要求先释放活动项目名额 | +| 强制刷新、关闭或浏览器崩溃 | 丢弃未保存修改,重新打开时只加载最后一次后端 saved 状态 | +| 画布达到 50 个元素 | 阻止添加并提示删除元素 | +| 自动保存失败 | 显示未保存,允许重试 | +| 多标签或多设备版本冲突 | 旧页面只读,可本地导出一次,之后刷新后端最新版;P0-A 只验收同机多标签 | +| 定位被拒绝 | 回退到地点手动输入 | +| 地理编码失败 | 保留手动输入,不阻塞其他编辑 | +| 字体资源失败 | 显示模板不可用或明确回退,不能无提示换字形 | +| 导出失败 | 保留上次成功成品和当前项目 | +| 非当前支持版本 Chrome/Edge 或其他浏览器访问 P0-A | 直接显示浏览器不受支持引导页,不进入任何产品功能且不提供继续入口 | +| 非本机、局域网或远程地址尝试访问 P0-A | 无法连接本地后端,产品不显示远程或移动端入口 | +| P0-B 手机旋转屏幕 | 自适应重排,不丢失画布、撤销栈或未保存状态 | +| 超级管理员查看私有内容 | 按 super_admin 身份与告知版本持久记录首次提示确认;同版本不重复、文案升版后重新提示,每次点击“打开并记录审计”仍生成独立不可篡改审计 | +| 后端保存失败但浏览器可渲染 | 允许下载本地导出,不更新后端成品 | +| 项目没有导出成品 | 显示明确空状态,不自动导出 | +| Resend 达到硬上限或供应商接口不可用 | 暂停注册和需要新验证码的登录,显示真实状态并通知超级管理员 | +| 高德达到每月硬上限或不可用 | 停用 DYN004 自动定位,保留其他不依赖定位的能力 | +| 公开素材缓存达到 150 MB | 按 LRU 淘汰旧资源后再缓存,不写入任何私有状态 | +| 本机内容达到 80% 且低于 90% | 显示非阻断容量提醒,继续允许生图、保存、后台贴纸上传和缩略图生成,不自动删除 | +| 本机内容达到 90% 且低于 5 GB | 持续显示清理提醒,按下载成品、永久删除项目或显式清理符合条件的无引用贴纸文件、等待清理重测、恢复后继续生成的顺序引导,不自动删除 | +| 本机内容达到 5 GB | 不自动删除;阻止新生图、参考图持久化、最新成品保存、后台普通贴纸原图上传、缩略图生成和其他受管二进制写入,保留编辑、已有内容读取、下载和符合规则的显式清理 | +| LocalDataRoot 不可写或系统磁盘已满 | storage_status 进入 unavailable,停止新增写入、后台贴纸上传、缩略图生成和 AI 调用,保留已有内容查看、下载和删除 | +| 超级管理员清理后台上传贴纸历史文件 | 只允许清理无任何已发布版本或项目引用的原图和缩略图;要求确认并审计,物理清理和重新计量完成前不更新容量状态 | +| 素材根目录移动、改名或不可读 | 只暂停依赖该目录的素材功能并提示重新配置,不修改代码或自动复制素材 | +| 浏览器尝试直接读取本机素材或私有目录 | 阻止 file://、绝对路径和 File System Access API 访问,要求通过本地后端资源 ID 接口 | + +## 22. 场景式验收标准 + +本节 AC 按当前交付阶段分为三类: + +| 分类 | AC 范围 | 当前 P0-A DevelopmentPlan 处理规则 | +| --- | --- | --- | +| P0-A 必验 | AC-01–07、AC-09–14、AC-16–22、AC-24–25、AC-27–35、AC-39–53、AC-55–56 | 必须拆入当前开发与验收任务 | +| 通用规则、当前只验 P0-A 子集 | AC-15、AC-23、AC-36、AC-38 | 只拆入各场景明确写出的 P0-A 操作和预期;P0-B 子集留待未来单独立项 | +| P0-B 门槛与未来验收 | AC-08、AC-26、AC-37、AC-54 | 不属于当前 P0-A 开发交付,不得拆入当前迭代,也不得阻止 P0-A 完成 | + +DevelopmentPlan 按 AC 编号拆任务时必须先应用本表,不能因为某个 AC 位于同一章节或包含完整 P0 描述,就把 P0-B 能力纳入当前 P0-A。未来 P0-B 单独立项后仍须执行其专属 AC 及通用场景中的 P0-B 子集。 + +### AC-01 有效邀请注册 + +前置条件:超级管理员创建了未过期且未达使用上限的邀请码,当前阶段占用名额的普通账号总量未达上限。 + +操作: + +1. 新用户进入独立注册入口并输入邀请码和邮箱; +2. 系统在发送验证码前确认邀请码有效; +3. 用户输入有效验证码并填写创作署名和社交 ID; +4. 用户勾选同意当前版本《内测使用与隐私告知》; +5. 系统原子创建账号并占用一次邀请码。 + +预期: + +- 账号创建成功; +- 默认获得 10 点; +- 邀请码只增加一次已使用次数; +- 登录状态保持 30 天; +- 进入创作工作台。 + +### AC-02 无效邀请注册 + +前置条件:邀请码不存在、过期、停用或已达使用上限。 + +操作:用户在注册入口提交邀请码和邮箱。 + +预期: + +- 注册失败; +- 不发送验证码; +- 不创建账号; +- 不赠送点数; +- 显示明确原因。 + +### AC-03 生成成功 + +前置条件:用户 available_balance 足够,当前没有运行中任务。 + +操作: + +1. 选择启用模型; +2. 输入提示词; +3. 选择比例和合法参考图; +4. 从工作台“新建创作”提交生成。 + +预期: + +- 创建一个任务; +- 冻结正确点数; +- 成功后保存一张图片; +- 点数转为已扣除; +- 图片加入当前项目历史; +- 创建的是一个新项目; +- 用户可以继续编辑。 + +### AC-04 生成失败退款 + +前置条件:上游失败、超时或安全拒绝。 + +操作:用户提交生成并等待结果,然后从失败页重试。 + +预期: + +- 任务进入 failed 或 rejected; +- 冻结点数全部释放; +- 不产生成功图片; +- 提示词和草稿项目保留; +- 重试在同一项目内只新增 GenerationJob,不创建第二个项目; +- 用户也可以返回工作台主动“新建创作”创建其他项目。 + +### AC-05 并发限制 + +前置条件:账号已有 queued 或 running 任务。 + +操作:用户再次点击生成。 + +预期: + +- 不创建第二个任务; +- 不再次冻结点数; +- 提供返回当前任务的入口。 + +### AC-06 生成历史上限 + +前置条件:项目已有 10 张成功生成图。 + +操作:用户从该项目的“继续生成”入口尝试再次生成。 + +预期: + +- 阻止提交; +- 提示先删除旧历史图; +- 删除非当前底图后可继续生成。 +- 用户仍可通过工作台“新建创作”创建其他比例的新项目。 + +### AC-07 更换底图 + +前置条件:画布已有文字、普通贴纸、色卡和动态贴纸。 + +操作:用户切换到另一张项目历史图。 + +预期: + +- 所有覆盖元素保留; +- 普通文字和贴纸位置不变; +- 新底图比例与项目固定比例一致; +- 色卡重新提取五色; +- 新底图调色状态使用默认值; +- 操作可撤销并自动保存。 + +### AC-08 P0-B 文字模板全量加载 + +前置条件:准备从 P0-A 升级到完整 P0。 + +操作:系统读取四类 catalog.csv 并完成全量模板验证。 + +预期: + +- 展示 145 个花字、119 个标题、51 个标签和 17 个简约模板; +- 共 332 个; +- FLOWER007 和 FLOWER025 不因缺少 ZIP 被判为失败; +- 列表按稳定 ID 和 catalog 顺序工作; +- 白名单外模板可以按批次进入 internal_preview 或 passed,未授权普通账号不能发现; +- 全部通过后一次性启用,不逐批向普通用户开放。 + +### AC-09 文字内容与换行 + +前置条件:用户已选择文字模板。 + +操作:输入两行文字并点击完成。 + +预期: + +- 画布显示两行; +- 换行保存到项目; +- 重新打开项目后内容一致; +- 导出内容一致。 + +### AC-10 模板切换 + +前置条件:用户已输入多行文字并修改样式。 + +操作:切换到另一文字模板。 + +预期: + +- 文本和换行保留; +- 使用新模板默认字体和样式; +- 画布位置、旋转和整体尺寸保留; +- 可以撤销回原模板和样式。 + +### AC-11 字体覆盖 + +操作:完整 P0 用户从 86 个字体项中选择字体;P0-A 用户只使用白名单模板实际引用字体及 FONT081。 + +预期: + +- 实际渲染加载归档字体文件; +- 模板颜色、描边和装饰保留; +- 共享同一字体二进制的两个面板项仍保持各自入口; +- 重新打开项目后字体一致。 + +### AC-12 文字样式 + +操作:用户设置自定义填充色、描边色与宽度、背景色与透明度、对齐、行距和字距。 + +预期: + +- 行距只能在 1.0 至 1.9 间按 0.1 调整; +- 字距只能在 1.0 至 20.0 间按 1.0 调整; +- 背景透明度不影响文字; +- 点击完成后作为一次撤销记录; +- 导出与画布一致。 + +### AC-13 普通贴纸全量加载 + +操作:用户在弱网条件下打开贴纸面板,依次进入 part1 至 part25、滚动列表并把贴纸加入画布。 + +预期: + +- 共显示 1,407 张普通 PNG; +- 3 组重复资源在各自 part 中均存在; +- 不按文件名生成虚假标题; +- 用户可浏览和查看最近使用; +- 面板使用虚拟列表,未滚动到的缩略图不提前加载; +- 未加入画布的贴纸原图不因打开 part 而批量下载; +- 加入画布后才按需取得原图,滚动过程不持续增加 DOM 节点。 + +### AC-14 普通贴纸编辑 + +操作:用户添加贴纸并移动、缩放、旋转、翻转、调透明度、复制和调整层级。 + +预期: + +- 每个操作正确反映画布和导出; +- 操作可撤销; +- 自动保存后重新打开结果一致。 + +### AC-15 分阶段色卡 + +前置条件:画布有底图。P0-A 启用 COLOR001、COLOR002、COLOR008、COLOR016;P0-B 未来启用全部 16 种色卡。 + +操作:当前 P0-A 用户依次添加 4 种白名单色卡;未来 P0-B 再依次添加全部 16 种色卡样式。 + +预期: + +- 每个色卡提取相同的五色调色板; +- P0-A 的 4 种白名单布局均可生成;P0-B 子集验收时全部 16 种布局均可生成; +- 更换底图后自动更新; +- 裁剪、调色或滤镜变化不更新已有色卡; +- 面板信息图标准确解释其基于原始底图; +- 不因色卡提取额外上传底图。 + +### AC-16 动态时间贴纸 + +操作:用户添加时间贴纸,修改日期时间并提交。 + +预期: + +- 默认使用浏览器本地当前值; +- 使用用户修改后的值渲染; +- 重新打开项目不自动改变; +- 导出为不可点击的静态内容。 + +### AC-17 动态地点贴纸 + +操作: + +1. 用户选择 DYN004; +2. Dada 在调用定位前说明原始坐标会保存并显示在成品中; +3. 用户确认并授权浏览器定位; +4. 系统解析地点; +5. 用户修改地点文字并插入。 + +预期: + +- 普通地点模板保存确认后的地点文字; +- DYN004 只提示一次并保存原始坐标; +- 用户拒绝 Dada 提示时不调用定位,也不自动插入 DYN004; +- 用户可改用仅保存地点文字的其他地点模板; +- 删除项目或注销账号后坐标按规则删除。 + +### AC-18 身份贴纸 + +操作:用户添加普通署名贴纸和带 @ 的社交 ID 贴纸。 + +预期: + +- 默认读取注册资料; +- @ 前缀不重复; +- 单贴纸覆盖不修改账号资料; +- 导出为静态视觉内容。 + +### AC-19 DYN012 + +操作:用户添加 DYN012。 + +预期: + +- 使用 FONT081 Lexend Deca; +- 模板仍可编辑和导出; +- 测试结果明确标注为替代字体; +- 不宣称使用 DIN_MediumAlternate.otf。 + +### AC-20 自动保存与撤销 + +操作: + +1. 用户在 1 秒内连续完成多次画布操作; +2. 执行撤销和重做; +3. 等待自动保存; +4. 模拟一次保存失败后恢复网络; +5. 记录当前后端 saved 状态,再断网创建未保存修改并尝试正常刷新; +6. 取消离开警告后继续修改,再分别模拟强制刷新和浏览器崩溃; +7. 重新打开项目。 + +预期: + +- 当前会话撤销和重做顺序正确; +- 同一时间只有一个保存请求,连续变化合并到最新状态; +- 后端保存最终已提交状态; +- 正常刷新被离开警告拦截并尝试最后一次保存; +- 强制刷新或崩溃后重新打开时,只恢复最后一次后端 saved 状态且撤销栈为空; +- 保存失败时不显示已保存,页面保持打开后自动重试; +- 浏览器持久化存储中不存在未保存项目副本。 + +### AC-21 项目删除与恢复 + +操作: + +1. 用户删除项目; +2. 把其他 active 项目补到 20 个,并在 deleted_at 后 720 小时内尝试恢复; +3. 删除一个 active 项目释放名额,再次恢复; +4. 再次删除并主动永久删除; +5. 另一个项目等待到 purge_at。 + +预期: + +- 删除后释放活动项目名额; +- 20 个 active 项目已满时恢复被阻止,待恢复项目继续保持 trashed,原 deleted_at 和 purge_at 不变; +- 释放活动项目名额后,purge_at 前可以恢复为 active; +- 主动永久删除后立即不可恢复; +- 到达 purge_at 后即使物理清理任务尚未完成,用户侧也不可恢复; +- 关联生成图、参考图、坐标和导出成品进入异步物理清理。 + +### AC-22 账号注销 + +操作:用户确认注销。 + +预期: + +- 所有会话立即失效; +- 账号和所有作品立即删除; +- 旧账号和作品无法恢复; +- 原邮箱可以使用新邀请码创建全新账号; +- 匿名事件和访问审计按 180 天规则保留且不能恢复用户内容。 + +### AC-23 导出 + +操作:在仍有未提交文字修改时确认提交,并分别在四种比例导出 JPG 和 PNG。 + +预期: + +- 像素尺寸准确; +- 无 Dada 水印; +- 画布元素无错位、缺字或裁切; +- 未提交文字经确认后进入撤销历史并出现在导出中; +- 每种格式只保留最新一份; +- P0-A 可在同一电脑再次下载;P0-B 可在任一受支持设备再次下载。 + +### AC-24 P0-A 本机访问边界 + +操作:在项目方 Windows 电脑启动本地后端,分别从该电脑的 localhost、该电脑的局域网 IP、同一局域网内另一设备和手机尝试访问;再在本机当前稳定版 Chrome、Edge、其他浏览器和不受支持的 Chrome/Edge 版本中检查产品入口,并尝试从不受支持引导页继续进入登录、项目、编辑器和运营后台。 + +预期: + +- localhost 在本机 Windows Chrome 和 Edge 当前稳定版中可用,并可完成注册登录、生图、编辑、项目管理和导出; +- 其他浏览器和不受支持的 Chrome/Edge 版本直接进入浏览器不受支持引导页,只说明支持环境及重新打开方式; +- 引导页没有“仍然继续”或其他绕过入口,且不能进入登录、注册、项目、编辑器、运营后台或任何产品功能; +- 本地后端只监听回环地址,通过局域网 IP、另一设备或手机均不能访问; +- P0-A 页面不显示手机、局域网、远程访问或跨设备继续编辑能力; +- 页面或设置区域显示“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。”; +- 不要求备案域名、HTTPS、公司服务器或对象存储即可完成 P0-A 验收。 + +### AC-25 管理员私有内容访问 + +操作:super_admin 首次进入私有内容区域并确认当前告知版本,随后在新标签页、重启浏览器和重新登录后再次进入;再升级告知文案版本并进入区域;最后从生成记录打开图片或完整提示词。 + +预期: + +- 内容不会在列表中默认展示; +- 首次进入私有内容区域时显示一次全局规则提示,确认状态按 super_admin 身份和告知版本持久记录; +- 同一告知版本在新标签页、新会话或其他本机受支持浏览器中不重复显示,告知文案升版后必须重新提示; +- 每个入口显示“打开并记录审计”; +- 不要求二次确认或查看原因即可打开; +- 自动产生包含管理员、目标、时间、内容类型和 expires_at 的 PrivateContentAccessLog; +- 全局提示是否已确认不影响每次打开均创建独立 PrivateContentAccessLog; +- 审计记录在 180 天内不能修改或人工删除,到期后由系统清理; +- 只有 super_admin 可以访问审计后台。 + +### AC-26 全量模板视觉回归 + +前置条件:P0-B 已经未来单独立项并进入内部验收环境。本场景不属于 P0-A 验收范围,缺少本场景结果不阻止 P0-A 完成,但必须阻止 AC-54 的 P0-B 普通用户开放。 + +操作: + +- 使用固定画布、默认文字和确定数据渲染全部 332 个文字模板、16 个色卡和 35 个动态贴纸; +- 生成截图; +- 自动比较并人工复核。 + +预期: + +- 所有模板均可加载; +- 字体、布局、换行、颜色、描边和装饰与归档参考结构一致; +- Windows Chrome/Edge、macOS Safari、iOS Safari 和 Android Chrome 当前稳定版结构一致; +- 元素边界偏差不超过 2px; +- 单通道差异超过 16/255 的显著像素不超过画布的 1%; +- style_02 至 style_16 和 DYN012 的已知近似或替代必须在报告中标明。 + +### AC-27 50 元素画布 + +前置条件:P0-A 或 P0-B 电脑完整编辑器。 + +操作: + +1. 在画布添加 50 个混合元素; +2. 多选并移动; +3. 导出; +4. 尝试添加第 51 个元素。 + +预期: + +- 前 50 个元素可编辑、保存和导出; +- 第 51 个被阻止; +- 页面不持续无响应; +- 不破坏已有项目。 + +### AC-28 余额不足 + +前置条件:用户 available_balance 低于所选模型的单次点数。 + +操作:用户填写合法提示词并尝试提交生成。 + +预期: + +- 生成按钮不可提交; +- 显示当前可用点数、冻结点数、本次所需点数和联系管理员的提示; +- 不创建生成任务; +- 不调用上游模型; +- 不产生点数流水或余额变化。 + +### AC-29 超级管理员调点与原子扣减 + +操作: + +1. 超级管理员为指定用户增加点数并填写原因; +2. 用户同时发起两个生成提交; +3. 在任务仍冻结点数时,超级管理员扣减超过 available_balance 的点数。 + +预期: + +- 每次超级管理员调整均形成独立流水,记录管理员、原因、变动前后余额和时间; +- 同一账号只创建一个生成任务并只冻结一次点数; +- reserved_balance 不因管理员调点变化; +- available_balance 可以因管理员操作变为负数并向用户显示; +- 生成并发和任务结算本身不会额外制造负数; +- 生成成功时冻结点数只提交一次,失败时只释放一次; +- 用户可以查看自己的调整和生成点数流水。 + +### AC-30 模型启停与配置 + +前置条件:三个模型均已建立独立 ModelConfig。 + +操作: + +1. 用户打开生图页并确认当前模型点数和限制; +2. 超级管理员修改该模型的点数、比例或参考图上限; +3. 用户不刷新旧页面直接提交; +4. 用户重新确认最新配置并提交; +5. 超级管理员先尝试停用当前默认模型但不提供合法替代模型,再在同一操作中指定另一个已启用模型作为新默认模型并停用原默认模型; +6. 先尝试保存缺失、非正整数或重复的 recommendation_priority,再为三个模型配置互不重复的正整数,并分别模拟后台默认模型运行时不可用、全部模型运行时不可用和外部服务恢复; +7. 超级管理员修改 error_mapping_profile,再修改模型端点或响应协议。 + +预期: + +- 旧页面提交被拒绝,不创建任务、不调用上游且不冻结点数; +- 页面显示最新配置并要求用户重新确认; +- 重新确认后才使用最新启用配置创建任务; +- 未提供合法替代模型时整个停用操作失败,原模型启停、默认和推荐优先级均不变; +- 提供另一个已启用模型后,停用原默认模型和设置新默认模型在同一原子操作中成功,系统始终恰好有一个已启用默认模型; +- recommendation_priority 只能使用唯一正整数,1 为最高,不允许并列; +- recommendation_priority 缺失、非正整数或重复时整个保存失败,不产生部分配置更新; +- 后台默认模型运行时不可用时,ModelConfig.enabled、is_default 和 recommendation_priority 均不改变,后端按优先级返回其他运行时可用模型;全部模型不可用时返回空推荐并进入既有生成禁用或失败状态; +- 外部服务恢复只恢复运行时可用状态,不改写后台默认模型或推荐优先级; +- 停用后该模型不可创建新任务; +- 停用前已经提交的任务继续完成并按提交时配置结算; +- error_mapping_profile 只能映射到固定 GenerationErrorCategory,保存后生成新的 config_version; +- 模型端点或响应协议变化后 contract_validation_status 立即重置为 unverified; +- 后台不显示完整 API 密钥。 + +### AC-31 素材上下架与版本发布 + +操作: + +1. 超级管理员上传合法的普通 PNG 或 WebP,指定 part、顺序并上架; +2. 用户把该素材加入项目; +3. 超级管理员将素材下架; +4. 检查上传原图和生成缩略图对 managed_content_bytes 的影响; +5. 分别在 storage_status 为 full 和 unavailable 时尝试上传原图、生成缩略图和读取已有贴纸; +6. 分别尝试清理仍被已发布版本或项目引用的文件,以及不再被任何已发布版本或项目引用的历史上传原图和缩略图; +7. 超级管理员尝试发布校验失败的复杂模板资源包,再发布校验通过的新版本。 + +预期: + +- 上架后的普通贴纸按指定 part 和顺序出现; +- 后台上传的普通贴纸原图和缩略图均计入 managed_content_bytes,产品只读规范素材库不计入; +- full 或 unavailable 时不能上传原图、生成缩略图或写入其他受管二进制内容,但已有贴纸仍可按权限读取; +- 下架后不能添加到新项目,但已有项目仍可渲染和导出; +- 非法格式、缺失文件或哈希不一致的资源被拒绝; +- 校验失败的复杂模板资源包不能启用; +- 校验通过的新版本可启用,且保留稳定模板 ID、版本号和发布记录; +- 后台不能直接修改复杂模板内部样式; +- 仍被任何已发布版本或项目引用的原图或缩略图不能清理;无引用历史文件只有在 super_admin 明确确认后才能清理; +- 清理请求、引用校验和结果形成不可修改的 AdminOperationLog,不发生自动清理; +- 只有物理清理完成并重新计量后,managed_content_bytes 和 LocalBackendStorageState 才更新。 + +### AC-32 P0-A 核心白名单与视觉一致性 + +前置条件:系统处于普通账号上限 10 个的 P0-A 阶段,普通用户没有内部预览授权。 + +操作:普通用户浏览文字、字体、色卡、动态贴纸和普通贴纸面板;再使用固定画布、默认文字、确定动态数据、固定视口和 DPR,分别在本机 Windows Chrome 与 Edge 当前稳定版中渲染全部 P0-A 核心白名单复杂模板并比较结果。 + +预期: + +- 只显示 FLOWER001–008、H001–008、TAG001–007、TAG051、SIMPLE001–008; +- 字体只显示上述模板实际引用项并包含 FONT081; +- 只显示 COLOR001、COLOR002、COLOR008、COLOR016; +- 只显示 DYN001–004、DYN007、DYN008、DYN011、DYN012、DYN015、DYN016; +- part1 至 part25 的 1,407 张普通贴纸全部显示; +- 白名单外复杂模板已经注册但对该普通用户不可发现; +- 第 11 个占用名额的普通账号被阻止创建; +- super_admin 不占普通账号名额。 +- P0-A 白名单文字模板及其字体、4 个色卡和 10 个动态贴纸在 Windows Chrome 与 Edge 中均可加载; +- 两个浏览器中的字体、换行、布局、颜色、描边、装饰和导出像素保持一致; +- 元素边界偏差、显著颜色差异和人工复核规则遵守 NFR-02; +- 本场景不要求 macOS Safari、iOS Safari、Android Chrome 或白名单外复杂模板通过。 + +### AC-33 认证状态机 + +操作:分别测试未注册、已注册、已注销和 suspended 邮箱在注册与登录两个入口的行为。 + +预期: + +- 未注册邮箱只可通过有效邀请码注册; +- 已注册邮箱重复注册时明确引导登录且不消耗邀请码; +- 未注册或已注销邮箱登录时明确引导注册; +- suspended 邮箱不能注册或登录,并明确提示联系管理员; +- 已注销邮箱可用新邀请码创建全新账号,旧数据不恢复; +- 账号创建与邀请码次数增加保持原子一致。 + +### AC-34 新建、追加与固定比例 + +操作: + +1. 从工作台连续使用两次“新建创作”; +2. 从其中一个项目使用“继续生成”; +3. 尝试在项目内切换比例。 + +预期: + +- 两次新建产生两个项目; +- 项目内继续生成只追加历史,不创建第三个项目; +- 追加生成只能沿用项目比例; +- 改比例入口引导新建项目; +- 一个项目达到 10 张不会阻止创建另一个项目。 + +### AC-35 保存失败、离开与本地导出 + +操作:用户在有未保存修改时使本地后端保存失败、尝试离开页面并导出,再分别强制刷新、关闭页面和模拟浏览器崩溃;另一次正常导出中模拟成品写入 LocalDataRoot 失败。 + +预期: + +- 未保存画布、提示词、私有图片引用和编辑内容只存在当前页面内存,不写 localStorage、IndexedDB 或 Cache Storage; +- 页面保持打开时自动重试; +- 站内离开提供保存并离开、放弃和取消; +- 后端保存失败时仍可本地下载当前 JPG/PNG; +- 正常导出由客户端合成,同一文件用于下载和写入 LocalDataRoot; +- 项目保存或成品持久化失败时,本地导出不更新后端 latest_exports; +- 强制刷新、关闭或崩溃后重新打开,只恢复最后一次后端 saved 状态; +- 浏览器持久化存储中不存在可用于恢复的私有项目草稿。 + +### AC-36 多标签与 P0-B 多设备冲突 + +前置条件:P0-A 只验收同一台 Windows 电脑中的多标签冲突。P0-B 另用电脑和手机执行的多设备冲突属于未来验收,不是 P0-A 完成条件。 + +操作:P0-A 在同机两个标签页同时基于同一 state_version 编辑;未来 P0-B 再使用不同电脑和手机重复该场景。较新的页面先保存,旧页面后保存。 + +预期: + +- 旧版本页面进入只读冲突状态; +- 不允许覆盖后端状态或另存项目副本; +- 允许本地导出一次且不更新后端成品; +- 用户确认后刷新到电脑保存的最新版本。 + +### AC-37 P0-B 手机横竖屏完整编辑 + +前置条件:系统处于 P0-B 内部验收环境,尚未要求向普通用户开放 P0-B。 + +操作:分别在 iOS Safari 和 Android Chrome 当前稳定版中编辑 20 元素常规画布和 50 元素压力画布,并在编辑中旋转屏幕。 + +预期: + +- 20 元素画布在竖屏和横屏均能完成全部编辑与导出,不出现持续无响应或操作丢失; +- 50 元素画布可完成选择、修改、保存和导出; +- 50 元素时允许关闭实时辅助线或降低编辑预览分辨率,但必须提示且不得改变最终导出; +- 旋转后画布、选区、撤销栈和未保存状态不变; +- 工具栏和面板不重叠、不溢出; +- 触控多选、循环选择、长按候选列表和框选可用。 + +### AC-38 无导出成品 + +前置条件:项目已有生成图但从未导出。 + +操作:P0-A 用户在本机打开成品下载区域;P0-B 在各受支持设备重复该场景。 + +预期: + +- 显示“暂无导出成品”; +- P0-A 本机提供进入编辑并导出以及下载原始生成图; +- P0-B 电脑和手机提供等价入口; +- 系统不自动生成默认 PNG 或 JPG。 + +### AC-39 注销后匿名保留 + +操作:注销一个曾生成图片且被超级管理员查看过内容的账号。 + +预期: + +- 个人资料、内容、项目、坐标和业务原 ID 立即删除; +- 匿名事件只保留随机主体、时间、模型、结果、错误类别和点数变化; +- 私有访问审计保留超级管理员身份,被查看用户和目标断链; +- 匿名事件在注销 180 天后删除,私有访问审计在各自访问事件发生 180 天后删除,注销不延长访问审计期限; +- 长期汇总统计不能回溯到个人或单次作品。 + +### AC-40 三模型网关契约 + +操作:对三个模型分别执行纯文字、参考图、四种比例、失败、超时和安全拒绝验证;在某一模型验证通过后,修改其端点、请求字段、参考图协议、响应结构、同步或异步模式、任务查询方式或错误协议中的任一项。 + +预期: + +- 每个模型形成独立脱敏证据; +- 明确同步或异步模式、图片返回格式和任务查询方式; +- 错误状态正确映射,冻结点数只提交或释放一次; +- 未验证模型的 contract_validation_status 不能设为 verified; +- 不因一个模型成功而推断另外两个模型协议相同; +- 任一契约相关配置变化后,对应模型立即重置为 unverified,必须重新执行本场景才能恢复 verified。 + +### AC-41 P0-A 本机测试准备 + +操作:在使用项目方控制的 1 个超级管理员账号和 1 个普通账号执行 P0-A 人工验收前完成发布检查。 + +预期: + +- 本地网页与本地后端可启动,后端只监听 localhost,局域网和其他设备无法访问; +- 第 18 节现有素材根目录可读,manifest 与素材文件校验通过,且没有复制第二套约 3.57 GB 全量素材; +- LocalDataRoot 位于项目目录和 Git 仓库之外、可写且未硬编码 Windows 用户名,LocalBackendStorageState 的 80%/90% 预警和 5 GB 限制可用; +- Resend 发信域名完成验证,QQ、163 和企业邮箱分别 20 封中至少 19 封在 2 分钟内送达; +- Resend 每天 80 封、每月 2,400 封的应用硬上限与暂停状态可用,未配置付费备用通道; +- 正式验收的普通账号与超级管理员认证真实调用 Resend;开发环境预置普通测试账号不能替代 AC-01、AC-33、AC-41 或 AC-49; +- 个人认证高德 Web 服务 Key 的定位、逆地理编码、绑定、安全限制和每月 1,000 次硬上限实测通过; +- AI 网关未开启自动充值,模型级、账户级和未知影响范围的余额不足运行时不可用规则可用,且不改写后台默认模型; +- AI 网关完整矩阵通过; +- 注册页展示并记录用户同意的《内测使用与隐私告知》版本; +- 注册告知和本地测试提示明确说明数据由当前 Windows 用户登录和文件系统权限保护、不提供应用层加密或云备份、机器损坏/重装/LocalDataRoot 删除后不可恢复,并提示主动下载重要图片; +- Dada 主动公开素材缓存的 150 MB 硬上限和私有状态禁存规则通过; +- AI 网关、Resend 和高德的必要外网 HTTPS 调用在本机可用,所有 API 密钥均未进入 PRD、Git、前端、manifest 或本机数据日志; +- 页面或设置区域显示本机数据无自动备份且不迁移的固定提示; +- 任一门槛失败时不得开始 P0-A 实际验收。 + +### AC-42 内部模板分批预览 + +操作: + +1. super_admin 创建一个测试批次并加入白名单外模板; +2. 只把项目方指定的一个普通测试账号授权到该批次; +3. 授权账号、未授权普通账号和 super_admin 分别访问素材面板、公开 manifest 和服务端授权 manifest; +4. 尝试根据模板稳定 ID 推导本机资源接口或文件路径,并通过 file://、本机绝对路径和 File System Access API 绕过本地后端直接读取; +5. 撤销授权后再次请求内部预览 manifest 和资源接口。 + +预期: + +- 授权普通账号可以看到该批次中 internal_preview 或 passed 的模板; +- 未授权普通账号不能发现模板、批次或资源地址,公开 manifest 不包含 internal_preview_asset; +- 未公开资源路径不能仅由模板稳定 ID 推导; +- 授权账号只能使用资源 ID 通过本地后端授权 manifest 和登录鉴权资源接口加载资源,浏览器不能获得绝对路径,也不能通过 file:// 或 File System Access API 直接读取; +- 预览授权不提供后台、调点、模型配置、邀请码或私有内容权限; +- 撤销后普通账号立即失去新添加入口且不能再取得资源,已有项目仍按保存的资源版本渲染; +- internal_preview_asset 不进入 Dada 的公开素材持久缓存; +- 全部剩余复杂模板通过前,不向普通用户逐批公开。 + +### AC-43 失败草稿重试与批量删除 + +操作:用户在同一失败草稿中连续重试三次,再创建多个无成功图片的失败草稿并执行批量删除。 + +预期: + +- 同一草稿的三次重试产生三个新 GenerationJob,但项目数量不增加; +- 每个失败任务独立释放冻结点数; +- 多个 failed_empty 草稿可以一次移入回收站并释放活动项目名额; +- 包含任一成功生成图的项目不能混入失败草稿快捷批量删除; +- 用户主动回到工作台“新建创作”时仍创建新项目。 + +### AC-44 参考图生命周期 + +操作: + +1. 用户选择参考图但不提交并刷新页面; +2. 重新选择参考图并提交失败任务; +3. 更换参考图后在同一草稿重试成功; +4. 删除项目并等待物理清理。 + +预期: + +- 未提交参考图不上传,也不出现在 localStorage、IndexedDB 或 Cache Storage; +- 失败任务保存其提交时参考图关系; +- 成功重试保存新一组关系,不覆盖失败任务快照; +- 两组参考图始终为私有资源; +- 项目进入 purged 后关系立即不可访问,资源随后由异步任务删除。 + +### AC-45 内测账号名额 + +操作:P0-A 在同一 Windows 电脑上使用独立浏览器配置,先用项目方控制的 1 个 super_admin 和 1 个普通账号完成人工验收;测试程序临时创建第二个普通账号执行跨用户项目、图片、内部预览和本地鉴权接口越权测试,再清理该账号;随后创建普通 active、普通 suspended、普通 deleted 和 super_admin 账号,达到 P0-A 的 10 个普通账号名额后继续注册。 + +预期: + +- active 和 suspended 普通账号都占名额; +- super_admin 不占普通账号名额且不消耗普通邀请码; +- 临时账号只有普通用户权限,不能访问另一普通账号的项目、图片、内部预览授权或私有文件接口,测试完成后被清理; +- P0-A 不要求朋友或第二个真实普通用户参与; +- 第 11 个占用名额的普通账号不能创建; +- 普通账号注销为 deleted 后释放一个名额,新用户可注册; +- 后台历史累计创建账号数不因注销减少。 + +### AC-46 客户端缓存上限 + +操作:连续浏览足以超过 150 MB 的公开缩略图、模板转换产物和字体,并检查浏览器的 Dada 应用存储。 + +预期: + +- Dada 主动持久缓存不超过 157,286,400 字节; +- 超限前按 LRU 淘汰最久未用资源; +- 不预下载全量素材; +- 旧发布版本优先淘汰且当前画布所需资源仍可重新按需加载; +- 缓存中不存在 internal_preview_asset、提示词、私有图片引用、项目状态或未保存编辑内容; +- 用户主动下载的成品不计入缓存上限。 + +### AC-47 免费辅助服务硬停止 + +操作: + +1. 分别模拟 Resend 达到每天 80 封、每月 2,400 封; +2. 分别模拟 Resend 接口不可用、明确退信故障和其他供应商异常; +3. 在发布前人工送达测试中模拟某类邮箱 20 封中只有 18 封在 2 分钟内送达; +4. 模拟高德当月达到 1,000 次调用; + +预期: + +- Resend 达到任一上限后进入 paused_quota,暂停注册和需要新验证码的登录,不发送第 81 封或超过月上限的邮件; +- Resend 接口不可用、明确退信故障或其他供应商异常进入 paused_provider,通过 pause_reason 区分非敏感错误类别,不创建验证码发送成功假状态; +- 发布前人工送达测试未达到每类 19/20 的门槛时阻止 P0-A 发布,但不创建运行时质量暂停状态; +- 已存在且有效的 30 天会话继续使用,页面不显示验证码发送成功假状态; +- 系统不切换到腾讯云 SES 或其他收费邮件通道; +- 高德不发起第 1,001 次调用,DYN004 自动定位停用,其他手动地点或非地点功能可用; +- 系统不保存或计算运行时“最近 20 封”滚动送达率,不建立 EmailVerificationEvent; +- 所有暂停均记录原因并通知 super_admin,恢复必须在免费额度或供应商可用状态重新满足后进行。 + +### AC-48 三类素材与私有资源隔离 + +操作:在本机不同浏览器账号中,未登录访问公开素材 manifest 和缩略图,再尝试从稳定 ID 推导内部预览资源接口或本机路径,通过 file://、本机绝对路径和 File System Access API 直接读取,并使用未授权账号和其他用户会话访问内部预览素材、参考图、生成图与导出成品。 + +预期: + +- public_release_asset 可以公开只读并长期缓存; +- 公开 manifest 不包含 internal_preview_asset、private_user_asset 路径或可推导标识; +- internal_preview_asset 路径不得仅由稳定 ID 推导,只能通过本地后端授权 manifest 和登录鉴权接口访问; +- 未登录、未授权和错误用户均不能访问内部预览或私有资源;浏览器不能取得或直接使用本机绝对路径,file:// 和 File System Access API 均不能绕过本地后端鉴权; +- 正确所有者通过本地后端登录鉴权接口可以访问; +- 私有资源访问策略不影响已有项目正常渲染和下载。 + +### AC-49 超级管理员认证与恢复 + +操作:依次使用白名单外邮箱、白名单内新邮箱和第二个白名单邮箱访问独立后台入口;完成首次验证和后续登录,等待或模拟 30 天会话到期,再将一个管理员移出白名单并停用全部管理员;最后由部署人员修改当前阶段的后端安全配置并让 Dada 读取生效。 + +预期: + +- 白名单外邮箱在发送验证码前被拒绝,不创建管理员账号或会话; +- 白名单内邮箱首次验证后创建 super_admin,第二个白名单邮箱可创建独立备用 super_admin; +- 普通账号不能通过后台、邀请码或角色修改被提升为 super_admin; +- 管理员会话最长保持 30 天,主动退出、停用或移出白名单后立即失效; +- super_admin 不占普通账号名额; +- 允许全部管理员被停用,此后后台无自助恢复入口,只有部署人员修改当前阶段的后端安全配置后才能恢复; +- 部署人员不是 Dada 用户或后台角色,不能创建对应产品账号、登录后台或获得产品权限; +- 管理员创建、登录结果、停用、移出白名单和恢复均形成审计; +- 后端安全配置生效事件记录为 actor_type=system、actor_ref=backend_secure_config,且不包含部署人员身份、白名单内容或安全配置值。 + +### AC-50 后台敏感操作审计 + +操作:分别执行调点、账号状态变更、邀请码变更、模型配置、素材启停、普通贴纸历史文件清理及引用校验、预览授权、辅助服务恢复和私有内容查看,再尝试从后台修改或删除日志并推进时间超过 180 天。 + +预期: + +- 除私有内容查看外的敏感操作写入 AdminOperationLog; +- 普通贴纸历史文件清理的请求、引用校验、成功或拒绝结果均被审计; +- 私有内容查看只写入独立的 PrivateContentAccessLog,不在普通操作日志中重复保存私有正文; +- 超级管理员后台操作使用 actor_type=super_admin,后端安全配置生效事件使用 actor_type=system,不存在第三种 Dada 审计角色; +- 日志包含执行者、操作、目标摘要、结果、发生时间和 expires_at; +- 日志不包含验证码、API 密钥、会话令牌、完整提示词、图片内容或不必要的私有正文; +- 180 天内任何管理员均不能修改或人工删除日志; +- 到期后系统自动清理两类日志。 + +### AC-51 AI 网关余额影响范围 + +操作:分别模拟网关明确返回单模型余额不足、明确返回共享账户余额不足,以及只能确认余额问题但无法判断影响范围;随后补充余额并确认恢复。 + +预期: + +- 单模型影响只把对应模型标记为运行时不可用; +- 账户级影响把共享该 gateway_account_ref 的全部模型标记为运行时不可用; +- 未知影响范围按账户级处理,把共享账户的全部模型标记为运行时不可用; +- 三种失败都只释放一次相关 GenerationJob 的 reserved_balance,不扣用户点数; +- 系统不自动充值; +- 三种情况均不改写 ModelConfig.enabled、is_default 或 recommendation_priority; +- 未确认真实余额恢复前不能恢复运行时可用状态,恢复操作形成 AdminOperationLog。 + +### AC-52 免费额度下调 + +操作:模拟 Resend 或高德供应商免费额度低于当前应用侧 hard_limit,再分别尝试下调和提高运行上限,并尝试启用收费服务。 + +预期: + +- 运行配置可以在不扩大能力的前提下直接下调 hard_limit,无需升版 PRD; +- 下调形成配置审计和发布检查记录,且新上限立即阻止超额调用; +- 提高 hard_limit、启用收费通道或自动扩容必须先升版 PRD,不能只改后台或运行配置。 + +### AC-53 产品级生成错误分类 + +操作:对三个模型的提交和执行链路分别模拟 upstream_timeout、upstream_failed、safety_rejected、model_disabled、gateway_balance_insufficient、gateway_contract_invalid、reference_invalid、unknown_retryable 和 unknown_non_retryable,并让供应商返回包含原始错误码、错误正文、堆栈或内部地址的响应。 + +预期: + +- 三个模型把等价错误映射为相同的 GenerationErrorCategory,前台、GenerationJob、后台生成记录和统计使用同一 category_id; +- upstream_timeout、upstream_failed 和 unknown_retryable 进入 failed,并按规定提供原输入重试或稍后重试; +- safety_rejected 进入 rejected 并要求修改输入; +- model_disabled 在提交前阻止任务,并要求选择其他模型或等待; +- gateway_balance_insufficient 和 gateway_contract_invalid 优先于通用 model_disabled,要求选择其他模型或联系管理员; +- reference_invalid 要求更换或移除参考图; +- unknown_non_retryable 不提供重复重试入口,提示联系管理员; +- 提交前阻止时不创建任务或冻结点数,任务创建后失败或拒绝时只释放一次冻结点数; +- queued、running 和 succeeded 任务的 error_category 为空; +- 页面不显示供应商原始错误码、错误正文、堆栈或内部地址; +- 后台不能创建、重命名或删除固定分类。 + +### AC-54 P0-B 对普通用户开放门槛 + +操作:依次让 P0-A 验收、全量素材验收、AC-26、AC-37、白名单外模板 passed 状态、50 人账号上限、公开 manifest 隔离、任一模型 verified 状态、公司系统合并方案、正式存储后端、远程服务器、域名、HTTPS、正式数据隔离或移动端访问中的任一条件缺失,再尝试向普通用户开放 P0-B;最后满足全部条件并执行正式开放。 + +预期: + +- 任一门槛缺失时,普通用户继续处于 P0-A,只能看到核心白名单且普通账号上限保持 10; +- 内部开发、模板导入、内部预览和验收不受普通用户开放门槛阻止; +- 公开 manifest 始终不包含 internal_preview_asset; +- 任一模型在最后一次契约相关配置变化后不是 verified 时不得开放; +- AC-26 或 AC-37 尚未执行或未通过时不影响 P0-A 完成,但必须阻止 P0-B 向普通用户开放; +- 公司系统合并、正式存储、远程部署、域名、HTTPS、数据隔离和移动端访问任一方案未定版或未验收时不得开放; +- 全部条件满足后,白名单外 passed 模板一次性切换为 enabled,普通账号阶段上限切换并验证为 50; +- 不能通过只扩大人数、只开放部分模板或只完成手机编辑来宣称进入 P0-B。 + +### AC-55 P0-A 本机存储状态 + +操作:依次模拟 LocalDataRoot 正常可写且 managed_content_bytes 低于 4,294,967,296 字节、达到 4,294,967,296 字节、达到 4,831,838,208 字节、达到 5,368,709,120 字节、本次写入预计超过上限、LocalDataRoot 不可写和系统磁盘已满;在各状态下尝试生图、提交参考图、保存最新成品、上传普通贴纸原图、生成缩略图、写入其他受管二进制内容、编辑项目、读取和下载已有内容、只下载本地导出、永久删除项目,以及清理有引用和无引用的历史上传贴纸文件。 + +预期: + +- 低于 80% 且目录可写时 capacity_notice_level 为 normal、storage_status 为 active,正常写入并更新 managed_content_bytes 和 last_measured_at; +- 达到 80% 且低于 90% 时 capacity_notice_level 为 warning,显示非阻断提醒,生图、参考图、成品保存、后台贴纸上传和缩略图生成仍可继续; +- 达到 90% 且低于 5 GB 时 capacity_notice_level 为 critical,持续显示清理提醒,但仍不阻止生图、保存、后台贴纸上传或缩略图生成; +- critical 提示按“下载需要保留的成品、永久删除不需要的项目或由 super_admin 显式清理符合条件的无引用贴纸文件、等待物理清理和容量重新测量、恢复后继续生成”的固定顺序引导; +- warning 和 critical 均不自动删除文件; +- 达到或预计超过 5 GB 时进入 full,不创建新 GenerationJob、不调用 AI 网关、不冻结点数,也不持久化新参考图、latest_exports、后台上传普通贴纸原图、缩略图或其他受管二进制内容; +- full 状态不自动删除任何文件,仍允许编辑已有项目、读取和下载已有内容、只下载但不更新 latest_exports 的客户端导出、永久删除项目,以及显式清理符合条件的无引用历史上传贴纸文件; +- LocalDataRoot 不可写或系统磁盘已满时进入 unavailable,停止新增写操作、后台贴纸上传、缩略图生成和 AI 调用,但保留已有项目及素材的查看、下载和删除入口; +- 仍被任何已发布版本或项目引用的历史原图或缩略图不能清理;无引用文件的显式清理要求确认并形成不可修改审计; +- 物理清理完成前 managed_content_bytes、capacity_notice_level、storage_status 和 last_measured_at 均不因该清理更新;重新计量后只有容量低于硬上限且目录可写才恢复 active; +- 后台上传的普通静态贴纸原图和缩略图计入 managed_content_bytes;产品随安装包提供或通过第 18 节安全配置原地引用的只读规范素材库、应用代码、150 MB 浏览器公开素材缓存、数据库元数据、审计记录和用户下载目录均不计入。 + +### AC-56 P0-A 数据可丢失与不迁移 + +操作:确认系统未配置自动备份;主动下载原始生成图和 JPG/PNG 成品,尝试导出可编辑项目包、数据库、账号、点数、审计日志和素材关系;分别模拟删除 LocalDataRoot 和重新安装 P0-A;再启动 P0-B 公司系统合并环境并尝试自动或手动导入 P0-A 数据。 + +预期: + +- P0-A 不创建或宣称存在本机自动备份; +- 用户可以主动下载原始生成图和 JPG/PNG 成品,但系统不提供可编辑项目包、数据库、账号、点数、审计日志或素材关系的导出与导入入口; +- 删除 LocalDataRoot 或重新安装后,本机测试账号、项目、图片和日志不可由 Dada 自动恢复; +- 用户主动下载到下载目录的成品不随 LocalDataRoot 删除; +- P0-B 不自动或手动导入 P0-A 本机账号、项目、点数、审计日志或素材关系,下载图片也不被识别为正式迁移包; +- 页面或设置区域始终明确显示“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +## 23. 外部依赖与发布门槛 + +### 23.1 AI 网关 + +网关地址: + +https://oneapi.intelligrow.cn + +已确认三个模型标识在平台模型广场存在,但公开资料不足以确认图片端点、参考图字段和错误格式。 + +AI 相关契约定版前必须取得以下任一信息: + +- 平台提供的图片 API 文档; +- 脱敏 curl 或代码示例; +- 通过平台支持确认的请求与响应格式。 + +不得只根据模型名称假设三个模型使用相同端点。编辑器、素材和认证模块可以并行开发,但三模型 AC-40 未通过前,AI 适配器、ModelConfig 路由和点数结算联调不得宣告完成。 + +AI 网关使用项目方主动配置的既有额度,不得开启自动充值。能确认模型级余额影响时只把对应模型标记为运行时不可用;只能确认共享账户余额不足或无法判断影响范围时,把所有依赖同一 gateway_account_ref 的模型标记为运行时不可用。该运行时状态不得改写后台配置的默认模型、enabled 或 recommendation_priority。余额不足导致的任务失败必须释放对应冻结点数,不能通过后台点数继续制造无法支付的真实上游调用。只有确认真实余额恢复后才能由超级管理员恢复运行时可用状态,检测和恢复均按 GatewayBalanceState 与 AdminOperationLog 记录。 + +### 23.2 邮件服务 + +P0 使用 Resend 发送邮箱验证码。 + +- 使用自有域名或子域名作为发信域名; +- 按 Resend 要求完成 SPF 和 DKIM 验证; +- API 密钥只放在 P0-A 本地后端安全配置;P0-B 再迁移到公司正式安全配置; +- 只使用 Resend 免费档,不配置付费升级、按量超额或收费备用邮件供应商; +- 应用侧发送硬上限为每天 80 封、每个自然月 2,400 封; +- 达到任一硬上限后停止发送,暂停注册和需要新验证码的登录,已存在且仍有效的 30 天会话不受影响; +- 发布前对 QQ、163 和至少一种企业邮箱各发送 20 封测试邮件,每类至少 19 封在 2 分钟内送达; +- Resend 接口不可用、明确退信故障或其他供应商异常时进入 paused_provider,使用 pause_reason 区分非敏感错误类别,暂停需要新验证码的流程,不创建验证码假成功状态,也不切换到付费通道。 + +P0 不建立逐封 EmailVerificationEvent,也不保存或计算运行时“最近 20 封”的滚动送达率。发布前送达测试保留人工结果;未达到每类 19/20 的门槛时阻止 P0-A 发布,但不写入运行时 service_status。运行期只保留 ExternalServiceUsage 定义的额度计数、供应商可用状态和暂停原因,验证码明文不得进入任何日志。 + +为避免邮件联调阻塞编辑器等独立模块开发,DevelopmentPlan 可以定义仅开发环境可用的本地初始化脚本,预置一个普通测试账号。该脚本不得进入正常产品入口,不得在正式 P0-A 验收环境启用,也不能作为 AUTH-01、AUTH-02、AUTH-07、AC-01、AC-33、AC-41 或 AC-49 的通过证据。P0-A 正式认证验收必须真实调用 Resend;域名验证、发送或送达失败继续属于 P0-A 启动门槛失败。 + +Resend 当前公开免费档为每月 3,000 封、每天 100 封,P0 应用侧上限为其保守子集;发布前必须再次核对免费规则。如免费额度低于应用侧上限,可在不扩大能力的前提下直接下调运行 hard_limit,无需升版 PRD,但必须形成配置审计和发布检查记录;提高上限或启用收费服务必须升版 PRD。 + +参考:[Resend 域名要求](https://resend.com/docs/dashboard/domains/introduction)、[Resend Pricing](https://resend.com/pricing)。腾讯云 SES 不作为备用服务,因为其免费额度用完后会转为后付费;参考:[腾讯云邮件推送价格说明](https://cloud.tencent.com/document/product/1288/47930)。 + +### 23.3 地理编码服务 + +P0 使用个人认证的高德 Web 服务 Key 提供中国大陆地点的地理编码和逆地理编码。开放 DYN004 自动定位前必须确认个人 Key 的可用接口、月免费配额、QPS、绑定方式、IP 或域名白名单、安全限制和当前使用场景均符合要求。 + +- P0 应用侧每个自然月最多调用 1,000 次高德 Web 服务; +- 达到硬上限后停止调用,不购买流量包、不使用账户余额继续调用; +- 达到上限、高德不可用或 Key 核验不通过时停用 DYN004 自动定位; +- 其他仅需地点文字的模板继续允许手动输入,不阻塞不需要地点的编辑操作; +- 发布前必须再次核对官方免费配额;如免费额度低于应用侧上限,可在不扩大能力的前提下直接下调运行 hard_limit,无需升版 PRD,但必须形成配置审计和发布检查记录;提高上限或启用收费服务必须升版 PRD。 + +正式公开注册前必须重新评估是否迁移到公司认证主体。 + +参考:[高德地理编码文档](https://lbs.amap.com/api/webservice/guide/api/georegeo/)、[高德基础服务计费说明](https://lbs.amap.com/pages/base_service_price)。 + +### 23.4 P0-A 本机部署与存储 + +- P0-A 由本机网页、本地后端和 LocalDataRoot 组成,只在项目方当前 Windows 电脑运行; +- 本地后端只监听 localhost,不监听局域网地址,不配置公网映射、公司服务器、域名、备案或 HTTPS; +- P0-A 不使用 COS、公司对象存储或其他远程文件存储; +- 规范素材通过本地安全配置引用第 18 节的现有根目录,按 manifest 原地只读复用,不复制全库; +- public_release_asset 由本地后端提供公开只读接口;internal_preview_asset 必须校验登录账号和 AssetPreviewGrant;private_user_asset 只允许所有者或经审计的 super_admin 通过本地鉴权接口读取; +- LocalDataRoot 默认使用当前 Windows 用户的应用数据目录,不放在 Git 仓库或项目目录,不硬编码用户名; +- LocalDataRoot 不做 Dada 应用层加密或自动备份,依赖当前 Windows 用户登录和文件权限;测试数据可丢失且不自动迁移到正式系统; +- 任何 API 密钥不得进入 Git、PRD、前端包、素材 manifest 或本机数据日志。 + +P0-B 的公司系统合并、正式存储后端、远程服务器、域名、HTTPS、数据隔离和移动端访问必须在后续 PRD 升版或 DevelopmentPlan 中重新定版。本版不得默认正式存储一定使用 COS。P0-A 与 P0-B 数据断代是已接受的预期结果:不提供 P0-A 可编辑项目包或业务数据的导出、导入和迁移,P0-B 不自动或手动导入 P0-A 的账号、项目、点数、审计日志或素材关系;用户主动下载的图片仅用于留存。 + +### 23.5 内测告知 + +注册页使用必选的简短《内测使用与隐私告知》,至少覆盖 AI 网关、Resend、高德、P0-A 本机存储、超级管理员访问、DYN004 坐标、账号注销、180 天匿名事件及审计日志规则。告知必须明确 P0-A 数据依赖当前 Windows 用户登录和文件系统权限保护,Dada 不提供应用层加密或云备份,机器损坏、重装或 LocalDataRoot 被删除后不可恢复,重要图片需要用户主动下载。P0-A 本地测试页或设置区域必须同时显示固定提示:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +### 23.6 UI 设计 + +页面布局、色彩、图标、滤镜视觉、响应式细节和控件样式由 UIDesign.md 定义。UIDesign.md 不得覆盖或弱化本 PRD 的状态机、权限、异常行为、验收文案和禁用入口要求。UI 设计与 PRD 冲突时以本 PRD 为准;若产品确需改变行为,必须先升版 PRD,再同步更新 UI 设计。 + +## 24. 需求变更规则 + +以下变化必须升版 PRD,不能只在开发任务中临时决定: + +- 增减 P0 模型; +- 改变后台默认模型原子替换、recommendation_priority 唯一排序或运行时推荐不回写配置的规则; +- 改变点数和失败扣点逻辑; +- 改变项目或历史数量上限; +- 改变 25 个普通贴纸 part 的结构; +- 将普通文字、用户上传贴纸或多页图组加入 P0; +- 改变超级管理员查看私有内容的权限; +- 改变私有内容区域提示按 super_admin 身份与告知版本持久确认,或以该确认替代逐次 PrivateContentAccessLog; +- 改变定位坐标保存规则; +- 改变账号注销或项目删除期限; +- 改变 P0-A 白名单、10 个普通账号上限、实际验收账号组合或剩余复杂模板一次性公开规则; +- 改变 P0-B 对普通用户开放门槛; +- 为 P0-A 增加手机、局域网、远程访问、公司服务器或远程存储,或取消 P0-B 手机完整编辑; +- 允许不受支持浏览器绕过 P0-A 浏览器引导页进入产品功能; +- 新增、删除、重命名 GenerationErrorCategory,或改变其任务状态、点数与用户动作映射; +- 改变 150 MB 客户端主动缓存上限或允许持久化用户私有编辑状态; +- 改变 P0-A 的 local_filesystem 存储后端、LocalDataRoot 位置边界、80%/90% 容量预警阈值、5 GB 内容硬上限、无自动备份、无项目归档或不迁移规则; +- 改变后台上传普通贴纸原图和缩略图计入 managed_content_bytes、只读规范素材不计入、引用保护、显式清理或物理清理后重计量规则; +- 增加收费辅助服务、自动充值、自动扩容或提高 Resend/高德应用侧硬上限; +- 改变匿名事件、AdminOperationLog 或 PrivateContentAccessLog 的 180 天保留期; +- 从受控内测改为公开注册。 + +Resend 或高德供应商免费额度下降时,允许在不扩大产品能力的前提下直接下调运行 hard_limit,并记录配置审计和发布检查结果,不要求仅为该下调升版 PRD。任何提高上限、增加收费服务、自动充值或自动扩容仍必须按上述规则升版。 + +## 25. 术语 + +| 术语 | 定义 | +| --- | --- | +| 底图 | 当前项目中作为画布背景的 AI 生成图 | +| 覆盖元素 | 文字模板、普通贴纸、色卡或动态贴纸 | +| 文字模板 | 包含可编辑文字、字体、装饰与样式规则的复合资源 | +| 普通贴纸 | 不依赖动态数据的 PNG/WebP 图像元素 | +| 色卡 | 从底图提取五色并按固定布局生成的参数贴纸 | +| 动态贴纸 | 在编辑时注入时间、地点或身份数据并静态导出的贴纸 | +| 生成历史 | 同一项目中保留的成功 AI 生成图片 | +| 创作署名 | 成品中显示的昵称,不等同于登录邮箱 | +| 社交 ID | 成品中显示的账号文本,不代表 Dada 内部账号标识 | +| 复杂模板 | 文字、色卡和动态贴纸等需要解析或参数渲染的资源 | +| P0-A | 仅在项目方当前 Windows 电脑通过 localhost 运行的单机测试版;由 1 个超级管理员账号和 1 个普通账号人工验收,跨用户隔离由同机测试程序临时账号验证,最多 10 个普通账号,只公开核心复杂模板白名单 | +| P0-B(完整 P0) | 与公司系统合并并完成正式存储、远程部署、数据隔离和移动端验收后,最多 50 个普通账号、全部规定素材一次性公开且支持手机完整编辑的阶段;开发与内部验收可提前进行 | +| 内部测试账号 | 由 super_admin 指定、只可预览授权素材批次的普通账号,不具有管理员权限 | +| 部署人员 | 当前阶段后端安全配置的维护者;P0-A 由项目方承担,P0-B 由公司既有运维流程承接;不是 Dada 用户或后台角色,没有 Dada 登录入口、后台会话或产品权限,配置生效时由 system 记录审计 | +| LocalDataRoot | P0-A 本地后端管理的数据根目录,默认位于当前 Windows 用户应用数据目录,独立于项目、Git 和只读素材目录 | +| LocalBackendStorageState | P0-A 本地后端文件存储的容量、预警与可用状态契约,不是浏览器 localStorage | +| 公开发布素材 | public_release_asset,P0-A 可通过本地后端公开只读接口加载和缓存的模板、字体、缩略图或普通贴纸 | +| 内部预览素材 | internal_preview_asset,不进入公开 manifest;P0-A 只能通过有效 AssetPreviewGrant、本地后端授权 manifest 和登录鉴权资源接口访问 | +| 用户私有资源 | private_user_asset,P0-A 只能由所有者或经审计的 super_admin 通过本地后端鉴权接口访问 | +| 主动持久缓存 | Dada 通过浏览器存储主动维护的公开素材缓存,硬上限 150 MB,不包括用户下载成品 | +| GenerationErrorCategory | 三个模型共用的固定产品错误分类,统一任务状态、点数处理、用户下一步和后台统计口径 | +| 可用点数 | available_balance,可用于新任务且可因超级管理员调点为负数 | +| 冻结点数 | reserved_balance,只能由对应生成任务提交或释放 | +| 受控内测 | 通过邀请码开放并受 P0-A 或完整 P0 人数上限约束的阶段 | diff --git a/UIDesign.md b/UIDesign.md new file mode 100644 index 0000000..2ce7050 --- /dev/null +++ b/UIDesign.md @@ -0,0 +1,1402 @@ +# Dada UI Design + +| 文档项 | 内容 | +| --- | --- | +| 文档版本 | 1.0 | +| 产品范围 | P0-A Windows 单机版 | +| 设计状态 | 低保真交互与界面规范 | +| 主要画布 | Windows 当前稳定版 Chrome / Edge,1920 × 1080 优先验收 | +| 产品需求权威 | PRD.md v1.10 | +| 功能与状态导航 | FeatureSummary.md 1.1 | +| 工程与接口约束 | DevelopmentPlan.md 1.1 | +| 本阶段产出 | 页面、布局、组件、状态、文案、焦点顺序和可访问性规范 | +| 本阶段不产出 | 高保真视觉稿、前端代码、移动端、P0-B、部署人员 CLI | + +输入文件冻结校验: + +| 文件 | SHA-256 | +| --- | --- | +| PRD.md | 31F93674DF1A90B557FEE3AA9E74FB084E246CA8FE09F6BD4B1DC9D56D606565 | +| FeatureSummary.md | 6F80E272AAB08A5525B54501D83F16A4F6A7A170596947BBC25F1DA54F2FE844 | +| DevelopmentPlan.md | 7C579E9DF8D00DA9333F587D05FC966123437EE68575D87D73C903C1487FA9D6 | + +## 文档使用规则 + +本稿只解释上游需求如何落到界面,不创建新的产品角色、状态、接口或错误动作。出现冲突时,PRD.md 是唯一产品需求权威;FeatureSummary.md 只用于导航;DevelopmentPlan.md 只约束已冻结的工程、接口和状态实现。 + +设计交付遵循以下边界: + +- UI 可以决定布局、视觉、图标、控件、焦点顺序、提示层级和未被冻结的具体中文文案。 +- UI 不得改变产品状态机、权限、错误动作、验收条件、API 语义、固定提示或数据范围。 +- 后端返回的稳定 message_key 决定错误语义;前端只把它映射到本稿规定的标题、说明和允许动作,不用字符串猜测错误。 +- 前端不得把已配置默认模型、运行时推荐模型和用户当前选择合并为一个状态。 +- 页面展示不得暴露密钥、完整本机路径、私有正文、私有图片内容或不必要的个人信息。 +- 本稿中的“卡片”仅用于重复项目、素材缩略项和弹层内的独立对象;页面区段本身不做层层嵌套卡片。 + +## 设计目标 + +Dada 是面向小规模内测创作者的本机图像创作工具。普通创作体验应直接、醒目、有作品感;后台和编辑器应安静、紧凑、可扫描。视觉表达不能掩盖本机数据风险、费用、模型状态和保存状态。 + +界面遵循六项原则: + +- **先给状态,再给动作**:生成、保存、容量、服务和冲突状态必须在动作附近可见。 +- **视觉有识别度,操作不过度装饰**:艺术表达集中在登录、注册和无项目工作台顶部。 +- **危险动作明确且可逆时优先可逆**:移入回收站与永久删除分离;不可逆操作使用对象名确认。 +- **不暗换用户选择**:模型失效、版本冲突和未提交导出都要求用户明确确认。 +- **本机边界持续可见**:固定本机数据提示不是首次启动提示,不可被永久关闭。 +- **同一语义同一组件**:相同状态、message_key 和错误动作在普通端、后台与辅助界面中保持一致。 + +## 视觉方向 + +### 主视觉 + +整体使用荧光黄、黑、白、冷灰的高对比系统。荧光黄用于主要动作、当前选择和有限的品牌面积;黑色用于标题、正文和关键轮廓;白与冷灰承担大面积操作背景。错误、成功和信息状态使用独立红、绿、蓝,不依赖黄色表达所有含义。 + +视觉特征: + +- 方角模块、硬边分隔、粗体中文标题、细密数据行。 +- 不使用大面积圆角、渐变、光斑、漂浮装饰球或卡片堆叠。 +- 控件圆角最多 4 px;大区段保持直角。 +- 页面级主标题不超过 28 px;高密度面板标题使用 16 至 20 px。 +- 数字、括号和机能符号只在艺术带中少量出现,不作为导航编号。 +- 所有动态信息预留稳定宽度,状态切换不推挤相邻控件。 + +### 原创机能风艺术带 + +艺术带只用于: + +- 普通用户登录与注册页顶部。 +- 普通用户首次进入且没有任何项目的工作台顶部。 + +艺术带不用于: + +- 已有项目的工作台。 +- 项目列表、项目详情、编辑器。 +- super_admin 登录与后台。 +- 浏览器阻断与启动失败等系统错误页。 + +后续高保真阶段制作原创位图,不复制参考图中的文字、标志或具体构图。视觉由巨大 DADA 字形、银灰材质碎片、细技术线、少量荧光黄切片和低密度标记组成。艺术带不得遮住页面 H1、表单、错误提示或主动作。 + +建议规格: + +- 桌面横幅比例约 3.4:1,高度 180 至 240 px。 +- 内容安全区左右各 48 px;主字形与装饰均不得越过安全区覆盖操作内容。 +- 静态图为默认方案;如后续加入轻微位移,必须响应减少动态效果设置,并提供完全静态替代。 +- 图片加载失败时显示白底、DADA 黑色字标和一条荧光黄分隔,不影响表单和导航。 + +## 设计令牌 + +### 颜色 + +| 令牌 | 色值 | 用途 | +| --- | --- | --- | +| Brand Yellow | #F2F400 | 主按钮、当前标签、关键品牌面 | +| Ink | #111111 | 主标题、正文、主轮廓 | +| Paper | #FFFFFF | 页面与画布操作区 | +| Cool Gray 050 | #F7F7F5 | 次级背景、表格隔行 | +| Cool Gray 100 | #E8E8E5 | 分区、禁用背景 | +| Cool Gray 300 | #B9B9B3 | 边框、占位 | +| Cool Gray 600 | #62625D | 次要文字 | +| Danger | #C92A24 | 错误、破坏性动作 | +| Success | #16794B | 已保存、恢复、完成 | +| Info | #1769AA | 加载、排队、信息提示 | +| Focus | #005FCC | 键盘焦点环 | + +使用约束: + +- Brand Yellow 上只用 Ink,不用白字。 +- Danger、Success、Info 不能只靠颜色表达,必须同时出现图标和文字。 +- 页面正文与背景达到 WCAG AA;大号艺术字不承载唯一业务信息。 +- 禁用态保持文字可读,不通过降低到近乎不可见的透明度表达。 + +### 排版 + +| 层级 | 字体 | 字号 / 行高 | 字重 | +| --- | --- | --- | --- | +| 页面标题 | Microsoft YaHei UI, Segoe UI, sans-serif | 28 / 40 px | 700 | +| 区段标题 | 同上 | 20 / 28 px | 700 | +| 面板标题 | 同上 | 16 / 24 px | 700 | +| 正文与控件 | 同上 | 14 / 22 px | 400 或 600 | +| 表格与辅助信息 | 同上 | 12 / 18 px | 400 | +| 艺术带字形 | 系统粗黑或后续位图 | 72 至 96 px | 800 | + +字间距固定为 0,不随视口宽度缩放字号。金额、点数、日期和版本号使用 Segoe UI 的等宽数字特性;不引入产品包之外的远程字体。 + +### 间距与尺寸 + +- 基础间距:4、8、12、16、24、32、48 px。 +- 顶部导航高 56 px;严重状态条最小高 36 px;固定本机提示条最小高 32 px。 +- 普通按钮高 36 px;主要按钮高 40 px;图标按钮为 36 × 36 px。 +- 表格行高 44 px;密集审计表格可用 40 px。 +- 表单输入最小高 40 px;多行提示词输入最小高 120 px。 +- 页面最大内容宽 1440 px;后台数据区可使用可用宽度,不用居中窄栏。 +- 弹层宽度分为 480、640、800 px;不超过可用视口高度,正文区域内部滚动,标题和动作固定。 + +### 图标 + +实现阶段优先使用项目采用的 Lucide 图标。熟悉的工具只显示图标并提供 tooltip 和无障碍名称;业务命令使用图标加文字。 + +| 动作 | 图标 | +| --- | --- | +| 返回 | ArrowLeft | +| 撤销 / 重做 | Undo2 / Redo2 | +| 保存 | Save | +| 下载 / 导出 | Download | +| 删除 | Trash2 | +| 恢复 | RotateCcw | +| 当前任务 | LoaderCircle 或 CircleCheck | +| 状态警告 | TriangleAlert | +| 冲突 | GitCompareArrows | +| 查看详情 | Eye | +| 审计访问 | ShieldCheck | +| 诊断 | Stethoscope | +| 外部引导 | ExternalLink | + +不手绘通用图标。加载图标旋转但不改变按钮宽度;减少动态效果时改为静态图标加“处理中”文本。 + +## 全局组件 + +### 按钮 + +- **主要按钮**:荧光黄底、黑字,用于页面唯一主动作。 +- **次要按钮**:白底、黑色 1 px 边框,用于返回、取消、筛选。 +- **文字按钮**:无边框,用于表格内轻量动作。 +- **危险按钮**:红色边框或红底,只有最终确认使用实心红底。 +- **图标按钮**:固定 36 × 36 px;必须有 tooltip 和 aria-label。 +- 处理中保持原宽,显示 spinner 与动词进行时,禁止重复提交。 + +### 输入与选择 + +- 输入框标签始终在控件上方;必填标记和错误说明不占用 placeholder。 +- 邮箱与验证码使用原生可识别的 input 类型和自动完成语义。 +- 模型、比例、格式等有限选项使用菜单或分段控件,不使用自由文本。 +- 二元设置使用复选框或开关;危险设置不使用仅图标开关。 +- 数字范围使用输入加步进器;JPG 质量使用滑杆与数值输入同步。 +- 字段错误紧邻字段,页面级错误位于标题下方;二者可同时存在。 + +### 标签与状态徽标 + +状态徽标为方角短标签,固定最小宽,不使用只有颜色的圆点。状态词与后端状态一一映射,不用“差不多完成”等模糊措辞。 + +### 通知 + +| 类型 | 位置 | 行为 | +| --- | --- | --- | +| 字段错误 | 字段下方 | 聚焦后仍保留,字段修正并重新校验后消失 | +| 页面错误 | 页面标题下 | 持续到重试成功或离开页面 | +| 场景状态条 | 受影响区段上方 | 服务恢复或场景退出后消失 | +| 全局容量条 | 普通端导航下 / 后台内容顶部 | critical、full、unavailable 持续显示 | +| Toast | 右上角 | 仅用于低风险完成反馈,不承载唯一错误 | +| 冲突遮罩 | 编辑区或配置区 | 阻断写操作,保留规定的读取、导出或刷新动作 | + +### 表格 + +- 表头固定,第一列可在横向滚动时固定。 +- 支持筛选的列在表头提供明确按钮,不把筛选藏在列名点击中。 +- 行动作统一放在最右侧“更多”菜单;高频唯一动作可直接显示。 +- 空表格保留表头和筛选器,正文显示空状态与下一步动作。 +- 加载时显示稳定行高骨架;失败时不清空上一次已成功数据,并标注其更新时间。 + +### 弹层 + +- 打开后焦点进入标题后的第一个可操作控件。 +- 焦点限制在弹层内;Esc 仅关闭非破坏性弹层。 +- 关闭后焦点返回触发按钮。 +- 破坏性确认显示对象名、影响范围和不可恢复后果。 +- 后端请求期间禁用关闭与重复提交;请求失败后恢复控件,保留用户输入。 + +## 全局状态语言 + +### 加载、空、失败和禁用 + +| 状态 | 视觉 | 默认动作 | +| --- | --- | --- | +| 正常 | 完整内容与可用主动作 | 执行当前流程 | +| 空 | 明确空原因、一个主动作、可选次动作 | 创建、上传或清除筛选 | +| 加载 | 稳定尺寸骨架或行内进度 | 允许取消时显示取消 | +| 失败 | 红色标题、可理解说明、稳定 message_key 映射 | 重试、返回或诊断 | +| 禁用 | 控件保持可读,旁边说明前置条件 | 前往满足条件的入口 | +| 冲突 | 对象版本与最新状态并列 | 按产品规则刷新、重试或仅导出 | +| 容量限制 | 全局或场景状态条加受影响动作说明 | 清理、下载或重试计量 | + +### 容量状态 + +LocalBackendStorageState 的状态与动作必须来自后端;前端不自行用浏览器剩余空间估算。 + +| 状态 | 全局表达 | 生图 / 参考图 / 成品保存 | 上传贴纸 / 生成缩略图 | JSON 项目编辑 | 已有内容查看 / 下载 / 删除 | +| --- | --- | --- | --- | --- | --- | +| normal,低于 80% | 不显示全局条 | 允许 | 允许 | 允许 | 允许 | +| warning,达到 80% | 受影响场景显示黄色提示 | 允许 | 允许 | 允许 | 允许 | +| critical,达到 90% | 持续紧凑状态条 | 允许,但提交前再次显示预计占用 | 允许,但上传前再次确认 | 允许 | 允许 | +| full,已达到 5 GB,或预计本次写入后超过上限 | 持续红色状态条 | 阻断新增受管二进制写入与 AI 调用;客户端只下载按上游规则保留 | 阻断 | 允许可提交的 JSON 编辑 | 允许 | +| unavailable,不可写或系统磁盘满 | 持续红色状态条 | 阻断新增写入与 AI 调用;已有文件可下载 | 阻断 | 后端写入失败,页面进入 failed | 允许查看、下载和删除入口 | + +预计本次写入后恰好等于 5 GB 时,允许本次容量预留和写入继续;写入提交后 storage_status 进入 full,并阻断后续新增受管二进制写入与 AI 调用。 + +容量提示中显示已用量、5 GB 上限、最后计量时间和“管理存储”入口。前端不得在清理排队时预先减少已用量;只有物理清理完成并重新计量后更新。 + +### 服务异常 + +- Worker degraded:生成表单和当前任务入口显示;不影响项目查看、编辑已有 JSON 和本地可完成的导出。 +- 无运行时可用模型:生成区持续显示,生成按钮禁用;不改写用户模型配置显示。 +- Resend 异常:只在登录、注册和后台服务页显示。 +- 高德异常:只在使用 DYN004 定位能力的素材场景和后台服务页显示。 +- AI 网关余额或可用性异常:在模型选择、生成提交、当前任务和后台服务页显示。 +- LocalDataRoot 容量异常:按上表全局表达。 +- 普通用户错误不显示密钥、绝对路径、原始上游响应或私有正文。 + +## 页面与入口地图 + +### 访问前门禁 + +浏览器访问 localhost 后首先进入支持检查。门禁通过后才可加载普通端或后台产品界面;失败只进入浏览器不受支持引导页。 + +### 普通用户 + +普通用户顶部导航: + +- 创作 +- 项目 +- 点数 +- 设置 + +右侧固定区域: + +- 当前生成任务入口:只要存在 queued 或 running 任务就持续显示。 +- 点数余额。 +- 账号菜单。 + +导航下方按需显示严重容量状态条。页面底部或固定底栏持续显示: + +> 测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。 + +普通用户页面: + +- 登录 / 注册 +- 创作工作台 +- 当前生成任务 +- 项目列表与回收站 +- 项目详情 +- 编辑器 +- 导出 +- 点数明细 +- 设置与账号注销 + +内部预览账号沿用普通用户全部页面,只在授权素材批次内多看到允许的 internal_preview 或 passed 内容。它不是新角色,不出现后台入口。 + +### super_admin + +super_admin 使用独立登录和独立会话。后台左侧导航: + +- 总览 +- 用户 +- 邀请码 +- 模型 +- 素材 +- 内部预览 +- 生成记录 +- 服务与存储 +- 审计 + +super_admin 不显示普通创作导航、创作工作台、项目或编辑器入口。 + +### 辅助系统界面 + +Windows 托盘和 Supervisor 提供: + +- 打开 Dada +- 查看运行状态 +- 打开诊断 +- 退出 Dada + +部署人员 CLI 不属于本稿。 + +## 权限矩阵 + +| 能力 | 未登录访问者 | 普通用户 | 内部预览账号 | super_admin | 辅助系统 | +| --- | --- | --- | --- | --- | --- | +| 浏览器不受支持引导 | 可见 | 门禁前可见 | 门禁前可见 | 门禁前可见 | 可打开支持页 | +| 登录 / 注册 | 可见 | 已登录后跳转创作 | 同普通用户 | 仅独立后台登录 | 不处理身份 | +| 创作与生成 | 不可 | 可 | 可 | 不可 | 只显示健康状态 | +| 已发布普通素材 | 不可 | 可 | 可 | 后台管理 | 只显示素材根健康 | +| 授权内部预览素材 | 不可 | 不可 | 可 | 管理授权与批次,不以普通用户身份消费 | 不可 | +| 项目、编辑器、导出 | 不可 | 可 | 可 | 不可 | 不可 | +| 后台运营 | 不可 | 不可 | 不可 | 可 | 不可 | +| 私有内容完整正文 / 图片 | 不可 | 不可 | 不可 | 告知确认后,逐次“打开并记录审计” | 不可 | +| 诊断摘要 | 不可 | 设置中只看用户相关简化状态 | 同普通用户 | 后台可看脱敏运营诊断 | 可看脱敏系统诊断 | + +账号被停用或注销后,服务端权限优先于已打开页面。页面收到拒绝时立即退出受保护内容并返回相应身份入口,不保留可交互的旧数据。 + +## 浏览器不受支持引导 + +**入口与权限** + +支持检查返回 BROWSER_UNSUPPORTED,或任何产品路由在门禁前被拒绝时进入。所有身份均只能看引导,不能继续使用产品功能。 + +**布局** + +- 白底全页,顶部 8 px 荧光黄规则线。 +- 中央 640 px 文字列,显示 DADA 字标、标题“当前浏览器无法使用 Dada”、原因、受支持品牌和版本。 +- 底部显示当前检测到的浏览器品牌 / major;识别不可用时只显示“无法可靠识别”。 +- 不加载产品主导航、项目缩略图或后台框架。 + +**控件** + +- “查看如何打开 Chrome / Edge”折叠说明。 +- “重新检测”只重新执行门禁,不提供绕过。 +- 若系统能力允许,提供“复制本机地址”;它不是继续使用入口。 + +**状态与动作** + +| reason | 文案重点 | 允许动作 | +| --- | --- | --- | +| platform_unsupported | 仅支持当前 Windows 电脑 | 查看引导、复制地址、重新检测 | +| brand_unsupported | 请使用已验收的 Chrome 或 Edge | 查看引导、重新检测 | +| version_unsupported | 当前版本不在本次发布支持范围 | 查看支持 major、重新检测 | +| identity_unavailable | 无法可靠识别浏览器 | 检查浏览器设置、重新检测 | + +焦点顺序:页面标题 → 折叠说明 → 重新检测 → 复制地址。错误摘要使用 assertive live region。页面没有“仍然继续”。 + +## 普通用户认证 + +### 登录 / 注册框架 + +页面顶部为原创机能风艺术带,下方为左对齐 480 px 表单列。艺术带不随字段展开改变高度。页面右上角只显示“管理员登录”文字入口,明确进入独立后台身份域。 + +登录与注册使用标签切换。切换时清除不属于目标模式的敏感字段,不把注册资料带到登录提交。 + +### 登录 + +**字段与动作** + +- 邮箱 +- 验证码 +- 获取验证码 / 倒计时后重新获取 +- 登录 + +登录模式不显示邀请码、创作署名、社交 ID 和内测告知。 + +焦点顺序:登录标签 → 邮箱 → 获取验证码 → 验证码 → 登录 → 注册标签 → 管理员登录。 + +**状态** + +| 状态 | 表达 | 允许动作 | +| --- | --- | --- | +| 初始 | 邮箱空,获取验证码禁用 | 输入邮箱 | +| 邮箱格式无效 | 字段错误 | 修正邮箱 | +| 发码中 | 按钮保持宽度并显示“发送中” | 等待 | +| 已发码 | 显示倒计时和发送目标的脱敏邮箱 | 输入验证码、倒计时后重发 | +| Resend 失败 | 页面错误,不清空邮箱 | 重试 | +| 验证码错误 / 过期 | 验证码字段错误 | 修正或重发 | +| 账号停用 | 页面错误 | 返回、联系管理员 | +| 登录成功 | 跳转创作 | 无重复提交 | + +### 注册 + +注册分为两个可见阶段,但仍是一个页面和一条注册流程。 + +**第一阶段** + +字段顺序固定为: + +- 邀请码 +- 邮箱 +- 获取验证码 + +获取验证码只有在邀请码和邮箱均通过本地完整性检查后可用。点击后使用既有发码请求,由后端校验邀请码有效性和名额;前端不新增邀请码预检 API。 + +后端确认邀请码有效、名额未满并成功发码后,才展开第二阶段: + +- 验证码 +- 创作署名 +- 社交 ID +- 《内测使用与隐私告知》阅读入口和必选同意框 +- 注册 + +第二阶段展开后,邀请码和邮箱保持只读摘要,并提供“修改”动作。修改会收起第二阶段并使已发挑战按后端规则失效或重新申请。 + +焦点顺序:注册标签 → 邀请码 → 邮箱 → 获取验证码 → 验证码 → 创作署名 → 社交 ID → 告知阅读 → 同意复选框 → 注册 → 登录标签。 + +**状态** + +| 状态 | 表达 | 允许动作 | +| --- | --- | --- | +| 邀请码未填 / 邮箱未填 | 获取验证码禁用,并说明缺少字段 | 补充字段 | +| 邀请码无效 | 邀请码字段错误,第二阶段不展开 | 修正邀请码 | +| 邀请码已用 / 过期 | 显示对应稳定错误语义 | 更换邀请码 | +| 名额已满 | 页面级阻断,不发送验证码 | 返回登录 | +| 发码成功 | 展开第二阶段 | 完成资料 | +| 验证码错误 / 过期 | 保留资料字段 | 修正或重发 | +| 告知未同意 | 注册按钮禁用,说明必选 | 阅读并勾选 | +| 最终复核失败 | 保留非敏感资料,显示具体稳定错误 | 按错误动作修正 | +| 注册成功 | 建立普通用户会话并进入创作 | 无重复提交 | + +告知正文必须覆盖 PRD 固定范围;界面不把长文塞入复选框标签。阅读弹层底部固定显示“我已阅读”,但只有返回表单后的复选框代表同意。 + +## 普通用户主框架 + +### 顶部导航 + +左侧为 DADA 字标和四个导航入口。当前入口使用荧光黄底与 3 px 黑色下边线,不能只靠颜色。右侧任务入口在 queued 或 running 时显示状态文字和进度;进入 succeeded、failed 或 rejected 后保留一次可查看结果提示,用户查看后恢复为隐藏。 + +顶部导航在页面滚动时保持可见。容量 critical/full/unavailable 状态条固定在导航下,不盖住页面内容。 + +### 本机数据提示 + +普通用户主框架底部持续显示固定文案: + +> 测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。 + +该条不可永久关闭。设置页重复显示更完整的数据风险说明和下载建议。屏幕阅读器首次进入页面时读出,站内导航时不重复打断。 + +## 创作工作台 + +### 无项目状态 + +**布局** + +- 顶部原创机能风艺术带,标题为“开始一张新作品”。 +- 艺术带下方直接进入生成表单,不把表单放进装饰卡。 +- 右侧或下方显示“当前任务”;无任务时显示简洁空状态。 + +**空状态动作** + +- 输入提示词。 +- 选择模型和比例。 +- 添加参考图(提交生成时上传)。 +- 查看预计点数。 +- 提交生成。 + +没有“浏览示例模板”之类未定义入口。 + +### 有项目状态 + +不显示艺术带。页面顶部为紧凑生成区,下方为最近项目与当前任务摘要。最近项目只展示真实项目数据;最多显示 6 个,之后使用“查看全部项目”。 + +### 生成表单 + +**布局与控件** + +- 提示词多行输入。 +- 模型选择菜单。 +- 比例分段控件:3:4、1:1、4:3、9:16。 +- 添加参考图区域,包含选择本地文件、标注“尚未提交,仅保留在当前页面”的缩略预览和移除;只有点击提交生成时才上传并绑定本次 GenerationJob。 +- 已确认点数成本。 +- 生成按钮。 + +首次新建创作时预选 recommended_model_id。界面同时区分: + +- “系统配置默认”:只在模型详情说明中展示 configured_default_model_id。 +- “当前推荐”:在推荐模型选项旁显示“推荐”。 +- “你的选择”:菜单当前值。 + +如果 recommended_model_id 为空,模型不自动选中,生成按钮禁用并显示“当前没有可用于新任务的模型”。 + +若用户已选模型后,该模型变为不可用于新任务: + +- 保留原选择并标红“当前不可用”。 +- 不切换到推荐模型。 +- 阻断提交。 +- 若后端返回新推荐,显示“改用推荐模型”按钮;用户点击后形成新的显式选择。 +- 重新显示成本和能力约束,用户再次确认后才可提交。 + +**提交前状态** + +| 条件 | 生成按钮 | 邻近说明 | +| --- | --- | --- | +| 提示词为空 | 禁用 | 请输入创作描述 | +| 模型未选 | 禁用 | 请选择当前可用模型 | +| 模型运行时不可用 | 禁用 | 保留原选择并显示原因、推荐替代 | +| 比例未选 | 禁用 | 请选择画面比例 | +| 参考图处理中 | 禁用 | 正在处理参考图 | +| 点数不足 | 禁用 | 显示所需与现有点数,入口到点数页 | +| 已有 queued / running 任务 | 不创建第二个任务 | 变为“查看当前任务” | +| Worker degraded | 禁用或按后端能力阻断 | 显示生成服务暂不可用 | +| storage full / unavailable | 禁用 | 显示容量动作 | +| 条件均满足 | 可用 | 显示确认成本与模型 | + +提交后立即进入当前任务视图,不乐观创建成功图片。 + +### 当前生成任务 + +当前任务既是顶部持续入口,也有独立详情抽屉或页面。抽屉宽 420 px,从右侧打开,不改变主页面状态。 + +| 状态 | 显示 | 允许动作 | +| --- | --- | --- | +| queued | 排队时间、模型、比例、冻结点数 | 关闭抽屉、返回项目 | +| running | 运行状态、开始时间、模型 | 关闭抽屉、查看项目 | +| succeeded | 结果缩略图、实际点数、项目入口 | 打开项目、下载原图 | +| failed | 错误标题、是否释放点数、允许动作 | 按错误映射重试或修改 | +| rejected | 拒绝原因、点数释放状态和允许动作 | 按错误映射修改提示词或参考图 | + +同一账号再次提交时若后端返回现有 queued / running 任务,界面打开该任务,不显示第二次冻结成功。 + +### 生成错误与提交阻断动作矩阵 + +具体 message_key 由后端返回。固定 GenerationErrorCategory 只有以下九类,前端不得新增、重命名或归并: + +| GenerationErrorCategory | 用户说明重点 | 主动作 | 任务与点数 | +| --- | --- | --- | --- | +| upstream_timeout | 上游响应超时 | 使用原输入重试 | failed,释放冻结点 | +| upstream_failed | 上游明确失败 | 使用原输入重试 | failed,释放冻结点 | +| safety_rejected | 内容未通过安全检查 | 修改提示词或参考图 | rejected,不保存输出并释放冻结点 | +| model_disabled | 模型因普通启停规则不可用 | 选择其他模型或等待 | 不创建任务、不冻结点 | +| gateway_balance_insufficient | AI 网关余额不足 | 选择未受影响模型或联系管理员 | 未创建则不冻结;已创建则 failed 并释放 | +| gateway_contract_invalid | 网关契约未验证或失效 | 选择其他模型或联系管理员 | 未创建则不冻结;已创建则 failed 并释放 | +| reference_invalid | 参考图格式、大小、数量或内容无效 | 更换或移除参考图 | 未创建则不冻结;已创建则 failed 并释放 | +| unknown_retryable | 未知但判断为临时错误 | 稍后重试 | failed,释放冻结点 | +| unknown_non_retryable | 未知且不应反复重试 | 联系管理员 | failed,释放冻结点 | + +以下情况是确定性业务阻断,不属于 GenerationErrorCategory: + +| 阻断条件 | 用户说明重点 | 主动作 | 任务与点数 | +| --- | --- | --- | --- | +| 点数不足 | 显示所需点数与当前可用点数 | 查看点数明细 | 不创建任务、不冻结点 | +| ModelConfig 版本变化 | 显示最新模型配置和成本 | 重新确认 | 不创建任务、不冻结点 | +| 已有 queued / running 任务 | 当前已有生成任务 | 查看当前任务 | 不创建第二个任务、不重复冻结 | +| 项目或生成历史达到上限 | 显示对应上限和清理要求 | 管理项目或删除旧历史图 | 不创建任务、不冻结点 | +| storage full / unavailable | 显示容量或可写性阻断原因 | 管理存储或打开诊断 | 不创建任务、不调用上游、不冻结点 | + +## 项目 + +### 项目列表 + +**入口与权限** + +普通用户及内部预览账号从顶部“项目”进入,只能看到自己的项目。 + +**布局** + +- 页面标题、active 项目计数和上限。 +- “项目”与“回收站”标签。 +- 搜索、排序和状态筛选。 +- 项目缩略图网格;卡片比例稳定,不因名称或状态变化改变尺寸。 +- failed_empty 快捷清理区只在存在候选时出现。 + +**项目卡片** + +- 当前底图或占位图。 +- 项目名、比例、更新时间。 +- 成功图数量。 +- 项目状态。 +- 打开主动作。 +- 更多菜单:重命名、移入回收站;只有允许的对象显示对应动作。 + +项目名最多显示两行,之后省略;完整名称通过 tooltip 和无障碍名称可读。 + +**状态** + +| 状态 | 表达 | 允许动作 | +| --- | --- | --- | +| 正常 | 网格与计数 | 打开、重命名、按规则删除 | +| 空 | “还没有项目” | 前往创作 | +| 筛选为空 | “没有符合条件的项目” | 清除筛选 | +| 加载 | 固定比例骨架 | 等待 | +| 失败 | 保留上次列表并标注旧数据 | 重试 | +| 达到 20 个 active 上限 | 顶部阻断提示 | 清理 failed_empty 或移入回收站 | +| 批量 failed_empty | 仅选择无成功图草稿 | 批量移入回收站 | + +含成功图项目不得进入 failed_empty 快捷批量操作。前端复核展示条件,后端仍做最终校验。 + +焦点顺序:页面标题 → 标签 → 搜索 → 筛选 → 排序 → 项目卡片 → 分页或加载更多 → failed_empty 清理。 + +### 回收站 + +- 显示删除时间和剩余保留时间。 +- 提供恢复和永久删除。 +- 恢复若触发 active 项目上限,显示阻断并指向现有项目管理。 +- 永久删除使用项目名确认;提交后显示清理进度。 +- 永久删除完成前不乐观减少容量。 + +### 项目详情 + +**布局** + +- 顶部:返回、可编辑项目名、项目状态、比例、更多菜单。 +- 主区:当前底图大预览。 +- 历史区:按时间展示最多 10 张成功图。 +- 操作区:继续生成、进入编辑器、下载原始图。 +- 最新导出区:每种格式最多展示最新一份;后端保存失败的本地下载不进入此区。 + +**状态与动作** + +| 状态 | 表达 | 允许动作 | +| --- | --- | --- | +| active 且有成功图 | 正常详情 | 满足生成前置条件时继续生成;编辑、下载 | +| failed_empty | 无成功图与最后错误摘要 | 修改并重试、移入回收站 | +| 已达 10 张成功图 | 历史上限提示 | 编辑、下载;不再追加生成 | +| trashed | 回收站标识 | 恢复、永久删除;不编辑 | +| state_version 冲突 | 版本冲突条 | 刷新后继续 | +| 加载失败 | 不展示部分可编辑数据 | 重试、返回列表 | + +项目比例创建后固定。继续生成不提供改变比例的控件。继续生成入口始终保留,但受项目 10 张成功图上限、模型运行时可用性、点数、同账号单一 queued / running 任务和 storage full / unavailable 约束;条件不满足时禁用入口,并在入口旁说明具体原因和允许动作。 + +## 编辑器 + +### 总体布局 + +编辑器使用固定三栏框架: + +- 顶部工具栏:56 px。 +- 左栏:280 px,素材与底图来源。 +- 中央:自适应画布工作区。 +- 右栏:320 px,当前对象或底图参数。 +- 底部状态行:32 px,缩放、对象计数和键盘状态。 + +1920 × 1080 下,画布完整可见且工具栏不换行。可用宽度缩小时左右栏各自内部滚动,不挤压画布到不可操作尺寸。 + +### 顶部工具栏 + +从左到右: + +- 返回项目。 +- 项目名与比例。 +- 撤销、重做。 +- 保存状态。 +- 预览。 +- 导出。 + +保存状态固定占用至少 128 px,覆盖: + +| 状态 | 显示文案 | 图标 / 色彩 | 行为 | +| --- | --- | --- | --- | +| dirty | 有未保存修改 | Circle / Ink | 允许继续编辑,离开需确认 | +| saving | 正在保存 | LoaderCircle / Info | 保持布局,禁止重复手动保存 | +| saved | 已保存 | CircleCheck / Success | 显示最近保存时间 | +| failed | 保存失败 | CircleX / Danger | 显示重试;离开与刷新按失败规则提示 | +| conflicted | 版本冲突 | GitCompareArrows / Danger | 编辑区转只读,进入冲突处理 | + +### 左栏 + +左栏使用文本标签,不使用数字编号: + +- 底图 +- 历史 +- 文字模板 +- 普通贴纸 +- 色卡 +- 动态贴纸 + +底图显示当前生成图和可用历史图。切换底图前显示说明:“覆盖元素会保留,底图编辑参数将重置。”用户确认后执行。 + +历史区按项目历史顺序显示,不能绕过项目 10 张成功图上限。素材列表使用虚拟滚动、懒加载和稳定缩略尺寸;内部预览账号只在授权素材上看到“预览”标识。 + +素材加载失败时: + +- 已加载素材继续可用。 +- 失败分区显示重试。 +- 只读素材根异常时显示“素材暂不可用”,不显示绝对路径。 +- 下架素材不得用于新添加;项目已有引用仍按保存版本渲染。 + +### 中央画布 + +- 画布背景为 Cool Gray 100,真实输出区域为白色或当前底图。 +- 固定宽高比容器避免图片加载时跳动。 +- 支持选择、移动、缩放、旋转、翻转、透明度、复制、层级、对齐吸附、循环选择、长按候选、多选和框选。 +- 对齐参考线使用 Focus 蓝,吸附结果同时有视觉和短暂文字提示。 +- 长按候选在指针附近显示对象列表,键盘用户可通过“循环选择”命令打开同一列表。 +- 覆盖元素达到 50 个时,新增相关动作禁用并说明上限。 + +明确不提供: + +- 图层面板。 +- 隐藏。 +- 锁定。 +- 编组。 + +层级调整只作用于当前选中对象,通过置顶、上移、下移、置底命令完成。 + +### 右栏 + +右栏标题随选择变化,但宽度不变: + +- 未选择对象:画布摘要和底图参数。 +- 选择底图:裁剪、适配、亮度、对比度、饱和度、色温、固定滤镜。 +- 选择文字:内容、模板允许的文字样式和对象变换。 +- 选择贴纸 / 色卡 / 动态贴纸:对象变换、透明度和素材允许参数。 +- 多选:对齐、分布、共同变换和删除。 + +数值控件使用滑杆与数值输入同步。拖动时画布即时预览,松开形成一个撤销记录;键盘步进结束后同样合并为合理的撤销单元。 + +### 编辑器状态 + +| 状态 | 中央区 | 顶部动作 | 允许动作 | +| --- | --- | --- | --- | +| 正常 | 可编辑 | 保存、导出可用 | 全部编辑 | +| 初次加载 | 固定画布骨架 | 导出禁用 | 返回项目 | +| 素材分区加载失败 | 画布保持可编辑 | 保存、导出可用 | 重试素材,编辑已有对象 | +| dirty | 可编辑 | 显示有未保存修改 | 保存、导出前确认 | +| saving | 可编辑但保存动作锁定 | 显示正在保存 | 继续本地编辑,按队列保存 | +| failed | 保留本地当前状态 | 显示保存失败 | 重试、按规则本地导出、返回需确认 | +| conflicted | 只读遮罩,显示本页版本和后端新版本 | 仅一次本地导出与刷新可用 | 本地导出一次、刷新最新版本 | +| storage full | JSON 保存仍按上游允许,二进制写入提示 | 导出可客户端下载 | 编辑、保存 JSON、客户端下载 | +| storage unavailable | 保留当前内存状态,保存 failed | 本地导出按上游可行性提示 | 查看、尝试本地下载、诊断 | + +同机多标签发生 state_version 冲突时,不提供合并、覆盖后端或继续编辑。旧页只读,允许一次本地导出当前内存版本,然后必须刷新本地后端最新版本。关闭冲突提示不能恢复编辑。 + +### 离开与崩溃边界 + +- dirty:离开弹层提供“保存并离开”“不保存离开”“继续编辑”。 +- saving:等待当前保存结果后再决定离开。 +- failed:明确未写入本地后端;提供重试、按规则本地导出和继续编辑。 +- conflicted:只允许一次本地导出和刷新最新版本,不提供覆盖。 +- 强制刷新、关闭页面或浏览器崩溃时,不承诺恢复未提交私有草稿。 + +## 导出 + +导出使用 640 px 弹层。 + +**控件顺序** + +- 格式分段控件:JPG / PNG。 +- JPG 质量:滑杆和数值输入;选择 PNG 时隐藏且不保留为可聚焦控件。 +- 输出尺寸与预计文件信息。 +- 未提交修改确认区。 +- “导出并下载”主按钮。 +- 取消。 + +如果存在未提交文字或样式修改: + +- 弹层显示“导出前需要提交当前修改”。 +- 用户确认后形成一次撤销记录,再用提交后的同一画布状态合成。 +- 用户取消时不提交,也不导出。 + +**结果矩阵** + +| 客户端合成 | 浏览器下载 | 后端最新成品保存 | 结果表达 | +| --- | --- | --- | --- | +| 成功 | 成功 | 成功 | “已下载并保存为最新成品” | +| 成功 | 成功 | 失败 | 明确“文件已下载,但未保存为最新成品”;latest_exports 不更新 | +| 成功 | 失败 | 未开始或失败 | 显示浏览器下载失败,允许重新下载同一次合成结果 | +| 失败 | 未开始 | 未开始 | 显示合成失败,返回编辑器检查 | +| 冲突页允许的一次导出 | 成功或失败 | 不覆盖后端 | 明确“仅下载本页版本,未写入项目” | + +下载与后端持久化必须来自同一次合成。后端保存失败不能撤回已成功的客户端下载,也不能假装 latest_exports 已更新。每种格式只保留最新一份后端记录。 + +焦点顺序:格式 → JPG 质量 → 修改确认 → 导出并下载 → 取消。结果消息通过 polite live region 宣读;失败通过 assertive。 + +## 点数 + +### 点数明细 + +**布局** + +- 当前可用点数。 +- 冻结点数说明。 +- 明细表:时间、事件、变动、任务 / 管理操作引用、余额。 +- 筛选:日期、事件类型。 + +**状态** + +- 空:显示“暂无点数变动”,不提供购买入口。 +- queued / running 冻结:用“已冻结”状态,不算作已扣除。 +- succeeded:显示实际结算。 +- failed / rejected:显示对应释放,避免看起来重复加点。 +- 加载失败:保留上次余额并标明更新时间,涉及新提交时仍以服务端实时校验为准。 + +upstream_cost_reconciliation 只用于后台诊断与本地监控,不改变 Dada 点数处理,也不在普通用户点数页显示为任务状态或点数状态。 + +普通用户不能调整点数。 + +## 设置与账号注销 + +### 设置 + +区段: + +- 个人资料:创作署名、社交 ID。 +- 账号:邮箱只读,登录与状态摘要。 +- 本机数据:固定提示、容量状态、主动下载建议、诊断摘要入口。 +- 注销账号。 + +本机数据区必须持续显示原文: + +> 测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。 + +同时说明数据依赖当前 Windows 用户登录和文件系统权限,不提供 Dada 应用层加密或云备份;机器损坏、重装或删除 LocalDataRoot 后不可恢复。界面只显示逻辑位置说明,不显示绝对路径。 + +### 注销 + +注销入口放在设置末尾危险区,不与退出登录相邻。流程: + +- 打开 640 px 确认弹层。 +- 展示影响范围、不可恢复内容和匿名保留规则。 +- 输入指定确认词或账号邮箱。 +- 再次获取验证码并验证。 +- 提交后显示处理中,不允许关闭重复提交。 +- 成功后结束普通用户会话并返回登录。 + +失败保留确认弹层和非敏感输入,按稳定错误显示重试动作。注销不提供将可编辑项目导入未来正式系统的承诺。 + +## super_admin 登录与框架 + +### 管理员登录 + +独立 URL、独立会话和独立 audience。页面使用白底、黑色字标和窄荧光黄顶条,不使用创作艺术带。 + +字段: + +- 管理员邮箱。 +- 验证码。 +- 获取验证码。 +- 登录。 + +邮箱不在管理员白名单时,发码前按服务端结果阻断。错误文案不暴露某邮箱是否属于普通用户。普通用户登录成功不能跳入后台。 + +焦点顺序:邮箱 → 获取验证码 → 验证码 → 登录 → 返回普通用户登录。 + +### 后台框架 + +- 左侧导航 216 px,固定在视口。 +- 顶部 52 px,显示页面标题、系统总体状态、管理员账号菜单。 +- 主内容区优先使用表格和无框分区。 +- critical/full/unavailable 容量条固定在主内容顶部。 +- 服务 degraded 可聚合为顶部“有异常”入口,但详细影响仍在对应页面。 +- 后台不显示创作、项目和编辑器入口。 +- 后台身份域不能把当前 super_admin 会话切换为普通用户。若同一自然人另有独立普通账号,必须退出后台并从普通用户登录入口进入普通端;普通账号与 super_admin 仍是独立身份,不能原地提升或共用会话。 + +## 后台总览 + +总览是运营状态摘要,不做营销式仪表盘。使用一列关键状态带和两列紧凑数据区: + +- 当前普通用户数与名额。 +- queued / running 任务。 +- 上游成本核对待人工核对数量和最早等待时间;该项来自 upstream_cost_reconciliation = pending_manual_review,不作为 GenerationJob 状态或点数状态显示。 +- 三模型配置默认、运行时可用数量和当前推荐。 +- LocalDataRoot 容量。 +- Resend、高德、AI 网关、Worker、素材根状态。 +- 最近后台操作。 +- 需要处理的贴纸清理任务。 + +每个摘要都链接到唯一负责页面。空状态代表“当前无待处理项”,不能与加载失败混淆。 + +## 用户管理 + +**布局** + +- 搜索与账号状态筛选。 +- 用户表:脱敏邮箱、创作署名、社交 ID、状态、点数、注册时间、最近活动。 +- 行动作:查看、停用 / 恢复、调整点数。 + +**用户详情抽屉** + +- 账号资料。 +- 项目与任务计数,不直接显示私有图片。 +- 点数摘要与最近流水。 +- 管理操作历史。 + +调整点数弹层要求: + +- 正负方向明确。 +- 数值、原因必填。 +- 提交前显示调整前后余额。 +- 处理中禁止重复提交。 +- 成功后更新余额和审计摘要。 + +普通后台查看不自动打开完整提示词或私有图片。需要私有内容时只能进入受保护的生成记录流程。 + +## 邀请码 + +页面含邀请统计、筛选和表格: + +- 邀请码脱敏显示。 +- 状态。 +- 创建时间、过期时间。 +- 使用账号和使用时间。 +- 创建人。 + +创建弹层只显示 PRD 允许字段。复制邀请码为低风险 Toast;禁用、过期或已用状态不能复制成“可用”。名额已满时创建或使用相关动作按后端规则禁用并说明。 + +## 模型 + +### 模型列表 + +每个模型一行,分别显示: + +- 模型 ID 与显示名。 +- configured enabled。 +- configured default。 +- recommendation_priority。 +- contract_validation_status。 +- runtime availability 及 reason、checked_at。 +- 当前是否为 recommended_model_id。 +- 成本、比例和参考图能力摘要。 +- config_version。 + +“默认”“推荐”“运行时可用”必须是三个独立列,不用一个徽标覆盖。 + +### 完整配置集合编辑 + +模型配置以完整集合弹层或页面编辑,不提供逐行立即保存。顶部显示 config_set_version。 + +规则: + +- 始终恰好一个已启用默认模型。 +- recommendation_priority 为唯一正整数,不能重复。 +- 停用非默认模型可直接纳入候选集合。 +- 停用当前默认模型时,同一操作必须先指定另一个已启用、合法的默认模型。 +- 保存以完整配置集合原子提交;任何字段失败整次不写入。 +- 保存时版本冲突映射 MODEL_CONFIG_VERSION_CONFLICT,用户刷新最新集合后重新编辑。 + +固定错误动作: + +| message_key | 界面动作 | +| --- | --- | +| MODEL_DEFAULT_REPLACEMENT_REQUIRED | 聚焦“替代默认模型”,不允许提交 | +| MODEL_DEFAULT_REPLACEMENT_INVALID | 标出替代模型不可作为默认的原因 | +| MODEL_RECOMMENDATION_PRIORITY_INVALID | 聚焦首个非正整数或缺失优先级 | +| MODEL_RECOMMENDATION_PRIORITY_CONFLICT | 同时标出重复优先级行 | +| MODEL_CONFIG_VERSION_CONFLICT | 整页阻断保存,提供刷新最新配置 | + +外部服务临时异常只更新运行时可用显示,不自动修改 enabled、is_default 或 recommendation_priority。恢复后 recommended_model_id 可重新派生,但界面不把结果写回配置。 + +## 素材 + +素材页分为: + +- 普通贴纸。 +- 素材版本与发布。 +- 历史文件清理。 + +### 普通贴纸 + +表格或缩略网格支持 part、状态、来源和稳定 ID 筛选。管理员上传流程: + +- 选择 PNG / WebP。 +- 填写稳定 ID、既有 part、顺序、启停和可选内部名称。 +- 上传前显示原图将写入 LocalDataRoot,缩略图由 Dada 生成。 +- 原图和缩略图都计入 managed_content_bytes。 +- 只读规范素材库明确显示“不计入本机 5 GB 内容额度”。 + +storage full / unavailable 时,选择文件和提交上传均禁用,缩略图生成不启动。已有素材仍可查看和按规则下架。 + +下架只阻止新添加;已有项目引用保持可渲染。页面不能提供“下架并立即删除文件”的组合动作。 + +### 素材版本与发布 + +- 版本表显示稳定 ID、版本、状态、白名单范围、引用计数和发布时间。 +- passed、internal_preview 与公开可见性分列展示。 +- 发布、撤回和启停使用后端合法状态;UI 不创建额外“半发布”状态。 +- 发布失败保留候选配置并显示具体 message_key。 +- 只读素材根校验失败时暂停依赖动作,提供重新配置指引或诊断入口,不自动复制素材。 + +### 历史贴纸文件清理 + +清理是独立流程,不能藏在下架动作中。 + +| 阶段 | 页面表达 | 允许动作 | 容量显示 | +| --- | --- | --- | --- | +| 候选查询 | 列出历史原图、缩略图、预计字节和引用摘要 | 选择候选、刷新 | 不变 | +| 确认 | 显示文件数量、总字节、不可恢复说明 | 确认并提交、取消 | 不变 | +| 引用复核冲突 | 整批拒绝,列出引用类型与对象 ID 的安全摘要 | 打开引用对象、刷新候选 | 不变 | +| 已排队 | 显示 cleanup job 和排队时间 | 查看任务 | 不变 | +| 物理清理中 | 显示处理进度,不允许重复提交 | 查看任务 | 不变 | +| 物理清理失败 | 显示已处理 / 未处理摘要和重试条件 | 按后端结果重试或诊断 | 重新计量前不乐观减少 | +| 重新计量 | 显示“正在重新计量本机内容” | 等待 | 使用旧值并标注 | +| 完成 | 显示实际释放与新容量状态 | 返回列表 | 以后端新计量为准 | + +固定错误动作: + +| message_key | 界面动作 | +| --- | --- | +| ASSET_HISTORY_REFERENCE_CONFLICT | 阻断整批清理,展示安全引用摘要 | +| ASSET_CLEANUP_CANDIDATE_STALE | 放弃旧候选,要求重新查询与确认 | +| STORAGE_CAPACITY_EXCEEDED | 阻断会新增受管二进制的动作,打开存储管理 | + +每个阶段在后台操作审计中有记录。无引用只是候选条件,不代表系统可自动清理;必须由 super_admin 显式确认。 + +## 内部预览 + +页面管理: + +- 预览批次。 +- 批次内素材版本。 +- 授权普通用户账号。 +- 生效时间与状态。 + +授权对象仍是普通用户账号。界面不得创建“内部预览管理员”或单独角色。普通用户端仅在已授权批次范围内多显示素材,并在缩略项上加“预览”标识;不出现管理入口。 + +取消授权后,用户不再能新添加预览素材;已有项目引用按上游版本规则处理。后台变更必须显示影响范围并写入后台操作审计。 + +## 生成记录与私有内容 + +### 默认列表 + +生成记录表默认显示: + +- 任务 ID。 +- 脱敏账号。 +- 模型。 +- 比例。 +- 状态。 +- 上游成本核对状态,与任务状态分列;pending_manual_review 显示为“待人工核对”。 +- 点数结算状态。 +- 创建与完成时间。 +- 错误类别。 + +默认隐藏: + +- 完整提示词。 +- 参考图。 +- 生成图片。 +- 其他私有正文。 + +列表筛选和普通详情摘要不触发私有内容访问。 + +### 私有内容告知 + +管理员首次进入私有内容区域时,后端按 super_admin 身份与当前告知版本校验。未确认时显示阻断弹层: + +- 标题“查看私有内容前请确认”。 +- 告知版本和生效时间。 +- 可查看范围、自动审计、最小化查看和禁止外传说明。 +- “确认并进入”主按钮。 +- “返回生成记录”次按钮。 + +确认成功后由后端持久记录。相同管理员、相同版本不因新标签页、重启、重登或换浏览器再次提示;告知版本升级后重新提示。前端 session 或 localStorage 不能作为确认权威。 + +若后端返回 PRIVATE_CONTENT_NOTICE_ACK_REQUIRED,立即退出私有区域并打开告知弹层。 + +### 逐次访问 + +告知确认不替代逐次访问审计。每个完整提示词、参考图或生成图片入口统一显示: + +> 打开并记录审计 + +动作流程: + +- 入口附近只显示对象类型、自动审计提示和最小化查看说明;不要求填写原因,也不增加逐次确认。 +- 用户点击。 +- 后端先写 PrivateContentAccessLog。 +- 审计成功后返回内容。 +- 审计失败则不打开内容。 + +图片默认使用低信息占位,不在 hover、列表预取、键盘聚焦或浏览器缓存预览时自动加载。完整提示词不进入页面初始 HTML。关闭内容后回到安全摘要,页面不把正文留在其他列表行。 + +## 服务与存储 + +页面分为五个无框区段: + +- Resend。 +- 高德。 +- AI 网关。 +- Worker / API。 +- LocalDataRoot 容量。 + +### 服务区段 + +每个服务显示: + +- configured / paused 等后台允许状态。 +- 最近健康检查时间。 +- 影响范围。 +- 当前错误安全摘要。 +- 允许的暂停、恢复或重新检查动作。 + +密钥只显示是否已配置和安全引用版本,不显示值。上游响应脱敏,不显示完整请求正文。 + +AI 网关按模型级、账户级或 unknown 影响展示。任何临时停用都只影响 runtime availability;页面显式提醒“不修改配置默认与推荐优先级”。管理员确认真实余额恢复后,按后端允许动作恢复运行时状态并审计。 + +### LocalDataRoot + +显示: + +- storage_status。 +- managed_content_bytes / 5 GB。 +- 80% 和 90% 阈值刻度。 +- last_measured_at。 +- 受影响动作清单。 +- 项目清理与历史贴纸清理入口。 +- 重新计量状态。 + +不显示绝对路径;只显示“当前 Windows 用户的 Dada 本机数据目录”等逻辑说明。只读规范素材库容量单独显示为参考信息,并标注“不计入 5 GB”。 + +full / unavailable 时,页面顶部和该区段同时显示阻断原因。恢复按钮只有在后端允许且已完成物理清理、可写检查和重新计量后出现;前端不能手工把状态改成 active。 + +## 审计 + +审计拆成两个同级标签,不混在一张表: + +- 后台操作审计。 +- 私有内容访问审计。 + +### 后台操作审计 + +覆盖用户状态、点数、邀请码、模型完整配置集合、运行时恢复、素材发布、贴纸清理各阶段、内部预览、服务操作等。列: + +- 时间。 +- 管理员。 +- 操作类型。 +- 对象安全摘要。 +- 结果。 +- request / operation ID。 + +### 私有内容访问审计 + +列: + +- 时间。 +- 管理员。 +- 被查看任务或项目的安全目标标识。 +- 查看内容类型。 +- expires_at。 + +两类表均不显示被访问的完整提示词、图片或密钥。180 天内不可编辑或删除;UI 不提供行内修改、批量删除或清空审计按钮。 + +加载失败时保留上次结果并标注时间;筛选导出若上游未定义则不提供。搜索只针对允许的标识与摘要,不对私有正文建前端搜索。 + +## Windows 辅助系统界面 + +辅助界面属于产品支持面,不属于运营后台或部署 CLI。 + +### 托盘菜单 + +状态图标和 tooltip 同时表达: + +- 正常运行。 +- 启动中。 +- API degraded。 +- Worker degraded。 +- 存储 full / unavailable。 +- 启动失败。 + +菜单顺序: + +- 打开 Dada。 +- 运行状态。 +- 打开诊断。 +- 重新启动服务,仅在 Supervisor 允许时显示。 +- 退出 Dada。 + +退出时按工程约束执行优雅关闭。托盘不显示账号私有内容、提示词或完整错误堆栈。 + +### 启动中 + +小型 480 px 系统窗口: + +- DADA 字标。 +- “正在启动本机服务”。 +- API、Worker、数据目录三个步骤状态。 +- “打开诊断”仅在超过正常等待或发生异常时出现。 +- 关闭窗口不等于退出服务;使用明确最小化到托盘行为。 + +### 启动失败 + +显示稳定标题、发生时间、安全错误摘要和 correlation ID。允许: + +- 重试启动。 +- 打开诊断。 +- 退出 Dada。 + +不显示密钥、绝对路径、私有正文或完整堆栈。 + +### 端口占用 + +标题“Dada 无法使用固定端口启动”。说明另一个程序或残留实例占用了端口。允许: + +- 检查是否已有 Dada 正在运行。 +- 重试。 +- 打开诊断。 +- 退出。 + +不提供在 UI 中修改端口,不暗切随机端口。 + +### API / Worker degraded + +当 API 可用但 Worker degraded: + +- 托盘显示警告。 +- 浏览器可进入项目、编辑器和已有内容下载。 +- 生成区显示服务异常并禁用受影响动作。 +- 诊断页显示 Worker 最近健康检查和有限重启结果。 + +当 API degraded: + +- 浏览器产品页显示全页连接状态,不继续展示可编辑旧数据。 +- 提供重试连接和打开诊断。 +- 托盘保持可操作。 + +### 诊断 + +诊断使用只读窗口或本地支持页,包含: + +- 发布版本、支持的 Chrome / Edge major。 +- API / Worker / Supervisor 状态与最近检查时间。 +- storage_status、容量比例和 last_measured_at。 +- 只读素材根校验状态。 +- 固定端口状态。 +- 最近有限数量的脱敏错误事件。 +- correlation ID。 +- “复制脱敏诊断”动作。 + +复制内容必须经过同一脱敏契约,不含: + +- 密钥或安全配置值。 +- Windows 用户名和绝对路径。 +- 邮箱全值。 +- 提示词、参考图或生成图片。 +- 原始上游请求 / 响应正文。 + +## 焦点与键盘规范 + +### 全局 + +- 每页第一个 Tab 目标为“跳到主要内容”。 +- 键盘焦点使用 2 px Focus 蓝外环和 2 px 间隔,在黄色、白色和灰色背景上均清晰。 +- 页面导航顺序与视觉顺序一致,不使用正 tabindex。 +- 图标按钮提供 aria-label;状态图标提供可读文字。 +- Toast 不是焦点目标;其中如有动作,动作进入正常焦点顺序。 +- 列表和表格不把整行及内部所有控件同时设为重复焦点。 + +### 编辑器 + +焦点区域顺序: + +- 跳到主要内容。 +- 顶部工具栏。 +- 左栏标签与素材。 +- 画布。 +- 右栏属性。 +- 底部状态。 + +画布获得焦点后,方向键执行已定义的对象微移;Esc 退出当前模式或选择;Delete 删除当前选中对象并保留撤销能力。所有快捷键必须能通过可见菜单完成,不在页面中放“使用说明”式常驻文字。 + +### 异步状态 + +- saving、queued、running、重新计量使用 polite live region,仅在状态变化时朗读。 +- 保存失败、冲突、浏览器阻断、容量 full / unavailable 使用 assertive live region。 +- 进度未知时不用虚假百分比,显示不定进度和开始时间。 + +## 文案与 message_key 绑定 + +以下文案固定,不得改写: + +| 场景 | 固定文案 | +| --- | --- | +| 本机测试数据 | 测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。 | +| 私有内容入口 | 打开并记录审计 | + +稳定 message_key 至少覆盖以下 UI 动作: + +| code / message_key | 展示位置 | UI 动作 | +| --- | --- | --- | +| BROWSER_UNSUPPORTED / browser.unsupported | 浏览器引导 | 硬阻断,无继续入口 | +| MODEL_CONFIG_VERSION_CONFLICT | 模型配置 | 刷新完整配置集合 | +| MODEL_DEFAULT_REPLACEMENT_REQUIRED | 模型配置 | 要求同一操作选择替代默认 | +| MODEL_DEFAULT_REPLACEMENT_INVALID | 模型配置 | 修正替代默认 | +| MODEL_RECOMMENDATION_PRIORITY_INVALID | 模型配置 | 修正优先级 | +| MODEL_RECOMMENDATION_PRIORITY_CONFLICT | 模型配置 | 修正重复优先级 | +| PRIVATE_CONTENT_NOTICE_ACK_REQUIRED | 私有内容区域 | 退出内容并要求确认当前版本告知 | +| ASSET_HISTORY_REFERENCE_CONFLICT | 贴纸清理 | 阻断整批清理 | +| ASSET_CLEANUP_CANDIDATE_STALE | 贴纸清理 | 重新查询并确认候选 | +| STORAGE_CAPACITY_EXCEEDED | 所有受管二进制写入 | 阻断写入并打开存储管理 | + +后端返回未知 message_key 时,使用安全兜底: + +- 标题“操作未完成”。 +- 显示 correlation ID。 +- 提供重试、返回或诊断中由接口允许的动作。 +- 不把未知错误映射成成功、点数已释放或可继续覆盖。 + +## 关键流程 + +### 普通用户注册 + +邀请码与邮箱完整 → 点击获取验证码 → 后端校验邀请码和名额 → 成功发码 → 展开验证码与资料 → 阅读并同意告知 → 最终注册复核 → 进入创作。 + +任何前置失败都不展开第二阶段。最终复核仍由后端完成,前端展开状态不代表邀请码已占用或注册必然成功。 + +### 新建创作 + +进入创作 → 读取模型列表与 recommended_model_id → 首次预选推荐模型 → 输入提示词与比例 → 可选参考图 → 确认模型配置版本和点数 → 提交 → 进入 queued / running 任务 → 成功进入项目,失败按错误动作返回输入。 + +模型在提交前失效时保留旧选择、阻断提交;用户主动改选并重新确认后才能继续。 + +### 编辑与导出 + +打开项目 → 加载画布和素材版本 → 编辑形成 dirty → 保存进入 saving → saved 或 failed / conflicted → 打开导出 → 如有未提交修改则明确确认 → 同一次客户端合成用于下载和后端最新成品保存 → 分别反馈下载和持久化结果。 + +### 默认模型替换 + +进入模型配置 → 编辑完整集合 → 尝试停用当前默认 → 必须选择新的已启用合法默认 → 校验唯一正整数 recommendation_priority → 提交带 config_set_version 的完整集合 → 原子成功或整次失败。 + +外部临时不可用不走此流程,也不修改配置集合。 + +### 私有内容访问 + +进入生成记录 → 默认只看安全摘要 → 打开私有区域 → 后端检查管理员身份与告知版本 → 必要时确认告知 → 点击具体对象“打开并记录审计” → 后端先写逐次访问审计 → 成功后才返回正文或图片。 + +### 历史贴纸清理 + +查询候选 → 选择与确认 → 后端复核所有有效引用 → 有引用整批拒绝 / 无引用创建清理任务 → 排队 → 物理清理 → 重新计量 → 更新容量状态 → 展示真实释放结果。 + +## 综合状态矩阵 + +| 页面 / 区域 | 正常 | 空 | 加载 | 失败 | 禁用 | 冲突 | 容量 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 登录 / 注册 | 可发码与提交 | 字段初始空 | 发码 / 登录中 | Resend、验证码、邀请错误 | 前置字段或告知未完成 | 不适用 | 非存储相关 | +| 创作工作台 | 可生成 | 无项目艺术带 | 模型 / 任务加载 | 服务、参考图、生成失败 | 模型、点数、单任务等前置不满足 | 模型配置变化需重确认 | critical 提示;full / unavailable 阻断 | +| 当前任务 | 状态与结果 | 无活动任务 | queued / running | failed / rejected 按稳定错误动作 | 不允许第二任务 | 不适用 | storage 错误按任务结果显示 | +| 项目列表 | active 项目 | 无项目或筛选空 | 卡片骨架 | 保留旧列表重试 | 20 项目上限 | state_version 刷新 | 删除后重计量,不乐观减少 | +| 编辑器 | 三栏可编辑 | 无成功图不能进入正常编辑 | 画布 / 素材加载 | 保存失败或素材失败 | 50 元素等上限 | 旧页只读、一次导出、刷新 | full 可按规则编辑;unavailable 保存失败 | +| 导出 | 下载并保存 | 不适用 | 合成 / 持久化中 | 下载与后端保存分别反馈 | 无画布或冲突次数用尽 | 冲突页只本地导出 | 后端失败不影响已下载文件 | +| 后台表格 | 数据与动作 | 无记录 | 稳定行骨架 | 保留旧数据 | 权限、状态前置 | 配置 CAS 冲突 | 顶部持续状态条 | +| 素材上传 | 上传并生成缩略 | 无上传素材 | 上传 / 处理 | 文件或编译失败 | 类型、状态、权限 | 稳定 ID / 版本冲突 | full / unavailable 阻断 | +| 私有内容 | 告知已确认且逐次审计 | 无记录 | 审计写入中 | 审计失败不打开 | 未确认当前版本 | 版本升级重新确认 | 不适用 | +| 贴纸清理 | 候选到完成 | 无可清理文件 | 排队 / 清理 / 重计量 | 失败按任务结果 | 无选择或无权限 | 引用或候选过期 | 完成重计量后更新 | +| 辅助系统 | 正常托盘 | 不适用 | 启动中 | 启动失败 | 不允许的重启动作隐藏 | 端口占用 | full / unavailable 告警 | + +## AC 界面追踪 + +| AC | 主要界面落点 | 必验 UI 行为 | +| --- | --- | --- | +| AC-24 | 浏览器不受支持引导、产品路由门禁、诊断 | 非支持品牌 / major / 识别失败均硬阻断,无继续入口 | +| AC-25 | 私有内容告知、逐次访问、私有内容访问审计 | 身份+版本持久确认;升版重提示;每次打开独立审计 | +| AC-30 | 模型完整配置集合、创作模型选择 | 唯一默认、优先级、原子替换、运行时推荐分层 | +| AC-31 | 普通贴纸上传、历史文件清理、容量条 | 原图 / 缩略图计量;full / unavailable 阻断;显式清理 | +| AC-35 | 项目详情、编辑器保存、导出 | 项目与成品保存失败动作清晰,不假成功 | +| AC-36 P0-A 子集 | 编辑器冲突 | 同机多标签旧页只读、一次本地导出、刷新最新版本 | +| AC-50 | 后台操作审计、私有内容访问审计 | 两类审计分离;清理全阶段有记录;正文不进入审计列表 | +| AC-51 | 模型运行时状态、服务与存储、当前任务 | 临时不可用不回写默认 / 启停 / 优先级;推荐重新派生 | +| AC-55 | 全局容量条、存储页、上传、生成、导出、清理 | 80% / 90% / 等于 5 GB / 超限 / unavailable 动作一致 | +| AC-56 | 本机数据提示、设置、诊断、导出 | 无备份、不迁移持续可见;主动下载保留在下载目录 | + +## 明确不做 + +本阶段不设计或暗示以下能力: + +- 手机、平板或移动端布局。 +- 远程访问、局域网访问、其他设备访问。 +- 跨设备同步、账号云端同步、云备份。 +- P0-B 公司系统、正式账号合并或数据迁移。 +- 可编辑项目归档、项目包导入导出。 +- 部署人员 CLI 或端口配置 UI。 +- super_admin 普通创作入口。 +- 内部预览独立角色或后台权限。 +- 图层面板、隐藏、锁定、编组。 +- 自动清理历史上传贴纸文件。 +- 模型失效后的静默替换。 +- 在浏览器不支持页继续进入产品。 +- 对只读规范素材库计入 managed_content_bytes。 +- 高保真视觉稿、艺术带最终图片和前端实现。 + +## 1080p 验收清单 + +高保真与实现阶段必须在真实 Windows 当前支持 Chrome / Edge 上逐页检查: + +- 1920 × 1080 下顶部导航、容量条、页面内容和固定本机提示不重叠。 +- 登录注册第二阶段完整展开后,主动作可通过滚动到达,艺术带不挤压表单。 +- 编辑器三栏、顶部保存状态和底部状态行不遮挡画布。 +- 最长中文状态、模型 ID、项目名和 message_key 映射不撑破控件。 +- 表格横向内容有明确滚动与固定关键列,不产生不可见行动作。 +- 弹层在可用视口内,标题与底部动作固定,正文可滚动。 +- 当前任务、容量条和服务异常同时出现时仍保持清晰层级。 +- 键盘可到达全部动作,弹层焦点不会逃逸,关闭后焦点正确返回。 +- 保存 failed / conflicted、storage full / unavailable、模型失效时没有隐藏可写入口。 +- 图片骨架使用稳定宽高比,加载、错误和实际图片切换不引起布局跳动。 +- 200% 浏览器缩放下不出现文字覆盖、不可关闭弹层或不可达主动作;允许页面滚动,不缩小字体规避问题。 +- Chrome 与 Edge 的焦点、下载和文件选择行为均按同一流程验收。 + +## 文档确认后的下一阶段 + +本稿确认后再进入低保真画布文件或可交互原型制作,并以本稿为页面与状态依据。高保真阶段补充原创机能风艺术带、真实素材缩略和像素级视觉 QA,但不得借视觉深化改变本稿绑定的产品状态、权限、错误动作、固定文案、API 语义或 AC。 diff --git a/tasks.manifest.json b/tasks.manifest.json new file mode 100644 index 0000000..38cc288 --- /dev/null +++ b/tasks.manifest.json @@ -0,0 +1,9361 @@ +{ + "schema_version": 1, + "document_version": "1.1", + "scope": "Dada P0-A Windows localhost only", + "generated_date": "2026-07-27", + "authority": { + "product": "PRD.md v1.10", + "engineering": "DevelopmentPlan.md v1.1", + "ui": "UIDesign.md v1.0", + "tdd": "tdd.md hash-locked file, internal version 1.4" + }, + "source_hashes": { + "PRD.md": "31F93674DF1A90B557FEE3AA9E74FB084E246CA8FE09F6BD4B1DC9D56D606565", + "FeatureSummary.md": "6F80E272AAB08A5525B54501D83F16A4F6A7A170596947BBC25F1DA54F2FE844", + "DevelopmentPlan.md": "7C579E9DF8D00DA9333F587D05FC966123437EE68575D87D73C903C1487FA9D6", + "UIDesign.md": "0C449DF833E5471843E17EB74F5B470EBC955F117CAF1D79AC9C0EF90DE763F0", + "tdd.md": "41EABEE330122FFB44752F834A97EAAE5C1CF35B99CB33B61AE6889077F96BFD", + "Dada-P0A-LowFi.pen": "A352C8F205E9399EB2E6306176253AF6FF65DDDBB9FDCEA2E2FE13E3C3459975", + "tasks.md": "A8DC1B0CA98D0ACB9F3186F15219839A753A69B2D4CEA9F9B4D92EFE1444A568" + }, + "module_boundary_policy": "modules are frozen DevelopmentPlan components; exact repository paths are intentionally not invented before WP-0 creates the monorepo layout", + "evidence_root": "artifacts/tdd/\u003crun_id\u003e/", + "task_count": 52, + "parent_family_count": 89, + "normative_case_count": 117, + "tasks": [ + { + "task_id": "TASK-WP0-01", + "work_package": "WP-0", + "title": "工程骨架与冻结依赖", + "dependencies": { + "source_text": "无", + "task_ids": [ + + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-09" + ], + "acceptance_criteria": [ + "AC-24", + "AC-46", + "AC-55", + "AC-56" + ], + "acceptance_criteria_role": "supporting", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-09、DevelopmentPlan 2.1、13.1", + "DevelopmentPlan WP-0 工程前置,supporting AC-24、46、55、56;不取得 AC 唯一主责" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "1.5 工程基础设施前置条件", + "2.1 固定技术基线", + "2.2 组件边界", + "3.4 环境关系", + "14.1 测试层级", + "15 WP-0" + ], + "modules": [ + "release-ci", + "web", + "api", + "worker", + "supervisor", + "shared-contracts" + ], + "implementation_boundary": "建立 pnpm monorepo、唯一 lockfile、React/Vite/TypeScript、Fabric、Fastify、TypeBox、Drizzle/better-sqlite3、Node Worker 和 .NET 8 Supervisor 最小可运行骨架;接通第 2.4 节脚本入口", + "parent_families": [ + "TDD-WP0-DEP-001" + ], + "case_ids": [ + "TDD-WP0-DEP-001-frozen-toolchain" + ], + "first_red": [ + { + "family_id": "TDD-WP0-DEP-001", + "assertion": "冻结依赖任一无法安装、加载或最小运行" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-DEP-001", + "assertion": "精确版本、TypeScript、React/Fabric、Fastify、better-sqlite3 native 和随包 Node 全通过;不私自替换技术栈" + } + ], + "task_green_gate": "精确版本安装、类型检查、React/Fabric 渲染、Fastify、native 模块和随包 Node 预检通过;失败时先修订 DevelopmentPlan,不得私自换栈", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-ci, web, api, worker, supervisor, shared-contracts.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-DEP-001-frozen-toolchain/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP0-02", + "work_package": "WP-0", + "title": "Schema、OpenAPI 与事件基线", + "dependencies": { + "source_text": "TASK-WP0-01", + "task_ids": [ + "TASK-WP0-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "GEN-14", + "NFR-05", + "PROJECT-04" + ], + "acceptance_criteria": [ + "AC-20", + "AC-30" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-05、DevelopmentPlan 6.1、6.6", + "PROJECT-04、GEN-14、NFR-05、DevelopmentPlan 6.2、AC-20、30" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "6.1 通用协议", + "6.2 API 分组", + "6.6 新增稳定错误码", + "14.1 测试层级" + ], + "modules": [ + "shared-contracts", + "api", + "web" + ], + "implementation_boundary": "建立 TypeBox 单一 Schema 来源、OpenAPI 3.1、生成客户端、统一错误信封、correlation ID、SSE 非敏感事件和 REST refetch/bootstrap", + "parent_families": [ + "TDD-WP0-API-001", + "TDD-WP0-EVT-001" + ], + "case_ids": [ + "TDD-WP0-API-001-schema-envelope", + "TDD-WP0-EVT-001-rest-refetch" + ], + "first_red": [ + { + "family_id": "TDD-WP0-API-001", + "assertion": "TypeBox、OpenAPI、生成客户端或错误信封漂移" + }, + { + "family_id": "TDD-WP0-EVT-001", + "assertion": "SSE 携带私有正文、被客户端当作业务真相或事件缺号后不恢复" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-API-001", + "assertion": "OpenAPI 3.1 snapshot 与运行验证一致;响应有 correlation ID;details 只含白名单字段且不泄露内部信息" + }, + { + "family_id": "TDD-WP0-EVT-001", + "assertion": "事件只含固定非敏感字段;客户端收到后 REST refetch;断线/缺号 bootstrap;配置和 runtime 版本分离" + } + ], + "task_green_gate": "OpenAPI snapshot 与运行校验一致;事件不承载业务真相或私有正文;断线/缺号恢复通过", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: shared-contracts, api, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-API-001-schema-envelope/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-EVT-001-rest-refetch/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP0-03", + "work_package": "WP-0", + "title": "回环访问与浏览器硬门禁", + "dependencies": { + "source_text": "TASK-WP0-02", + "task_ids": [ + "TASK-WP0-02" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-01", + "NFR-09" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-01、AC-24", + "NFR-01、AC-24、41", + "NFR-09、AC-24" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.6 浏览器支持硬门禁", + "6.4 HTTP 语义", + "12.1 本机安全边界", + "13.3 可访问性基础", + "14.7 AC-24" + ], + "modules": [ + "api", + "web-support-gate", + "supervisor" + ], + "implementation_boundary": "固定 127.0.0.1:43121、Host/Origin/CORS 防护、最小 gate shell、UA-CH 交叉校验、24 小时短期支持 Cookie、全产品路由前置 426 门禁", + "parent_families": [ + "TDD-WP0-BND-001", + "TDD-WP0-BRW-001", + "TDD-WP0-BRW-002" + ], + "case_ids": [ + "TDD-WP0-BND-001-loopback-origin", + "TDD-WP0-BRW-001-real-supported", + "TDD-WP0-BRW-002-hard-block" + ], + "first_red": [ + { + "family_id": "TDD-WP0-BND-001", + "assertion": "API 接受非回环绑定、错误 Host/Origin 或 CORS" + }, + { + "family_id": "TDD-WP0-BRW-001", + "assertion": "真实支持 Chrome/Edge 无法通过门禁" + }, + { + "family_id": "TDD-WP0-BRW-002", + "assertion": "其他品牌、错误 major、UA-CH 缺失/冲突或直调 API 可进入产品" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-BND-001", + "assertion": "只绑定 127.0.0.1:43121;拒绝局域网、其他 Host/Origin,不创建防火墙入站规则" + }, + { + "family_id": "TDD-WP0-BRW-001", + "assertion": "RELEASE.json 记录的两个真实 full version 通过并建立短期支持 Cookie,产品 API 每次复核品牌/major" + }, + { + "family_id": "TDD-WP0-BRW-002", + "assertion": "全部返回 426 BROWSER_UNSUPPORTED,只有最小引导,无登录、资源、DB 业务读取或外部调用,无绕过令牌" + } + ], + "task_green_gate": "局域网/其他 Host/Origin、非 Windows、其他品牌、错误 major、UA-CH 缺失/冲突和直调 API 均不可进入;支持分支最终 passed 等待 WP-7 真实记录", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, web-support-gate, supervisor.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "API", + "PKG-SEC", + "E2E", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:api", + "pnpm test:security", + "pnpm test:package", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-BND-001-loopback-origin/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-BRW-001-real-supported/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-BRW-002-hard-block/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "support-gate", + "role": "implementation", + "frames": { + "frame_id": "u1adni", + "frame_name": "07A 浏览器硬阻断", + "primary_task": "TASK-WP0-03", + "mapped_state_scope": "四种阻断 reason、支持品牌/major、最小诊断;bundle/API 无绕过;无业务空态、身份或容量读取" + }, + "applicable_states": { + "page_id": "support-gate", + "actor_entry": "所有主体,产品门禁前", + "normal": "四种阻断 reason、支持品牌/major、无继续入口", + "empty_loading": "N/A:引导无业务空态;身份不可判定属于阻断", + "failure_disabled": "bundle/API 均无法绕过;诊断仅最小字段", + "conflict_capacity_special": "N/A:不读取业务状态或容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/support-gate/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP0-04", + "work_package": "WP-0", + "title": "LocalDataRoot 与本机数据边界", + "dependencies": { + "source_text": "TASK-WP0-01", + "task_ids": [ + "TASK-WP0-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "AUTH-06", + "NFR-09" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41", + "AC-55", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.19" + ], + "requirement_ac_sources": [ + "AUTH-06、NFR-09、AC-24、56", + "NFR-09、17.19、AC-41、55、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.2 Supervisor 生命周期", + "3.3 离线配置命令", + "4.1 固定目录布局", + "4.2 文件标识与路径隔离", + "9.2 项目、历史、失败草稿和回收站", + "12.1 本机安全边界" + ], + "modules": [ + "api", + "sqlite", + "local-data-root", + "web" + ], + "implementation_boundary": "建立目录布局、路径规范化、资源 ID 隔离、data_missing、只读素材根校验、无备份/无归档/无迁移边界和固定本机风险提示接线", + "parent_families": [ + "TDD-WP0-DATA-001", + "TDD-WP0-DATA-002" + ], + "case_ids": [ + "TDD-WP0-DATA-001-root-validation", + "TDD-WP0-DATA-002-no-backup-migration" + ], + "first_red": [ + { + "family_id": "TDD-WP0-DATA-001", + "assertion": "LocalDataRoot 位于仓库/项目/下载/素材根,或丢失后自动重建" + }, + { + "family_id": "TDD-WP0-DATA-002", + "assertion": "产品出现自动备份、项目包、业务导入或 P0-B 迁移" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-DATA-001", + "assertion": "路径校验拒绝违规目录;已初始化根缺失进入 data_missing;浏览器只见资源 ID" + }, + { + "family_id": "TDD-WP0-DATA-002", + "assertion": "只允许下载原始生成图和 JPG/PNG;删除根或重装不可恢复;固定本机风险文案持续显示" + } + ], + "task_green_gate": "违规目录、符号链接和路径逃逸拒绝;已初始化根缺失不自动重建;浏览器不见绝对路径;产品无业务导入/项目包/恢复入口", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, local-data-root, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "PKG-SEC", + "E2E", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:security", + "pnpm test:package", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-DATA-001-root-validation/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-DATA-002-no-backup-migration/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "settings", + "role": "implementation", + "frames": { + "frame_id": "Ws5E0", + "frame_name": "05B 设置与本机数据", + "primary_task": "TASK-WP1-05", + "mapped_state_scope": "资料、本机数据、容量摘要和固定文案;加载/保存失败;full/unavailable;注销确认、处理中和返回登录" + }, + "applicable_states": { + "page_id": "settings", + "actor_entry": "active 普通/内部预览账号", + "normal": "资料、本机数据、容量摘要、固定文案、注销", + "empty_loading": "资料/状态加载", + "failure_disabled": "保存失败;注销确认/处理中禁用重复提交", + "conflict_capacity_special": "full/unavailable 状态与允许动作;注销后返回登录" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/settings/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP0-05", + "work_package": "WP-0", + "title": "容量状态、预留与文件一致性", + "dependencies": { + "source_text": "TASK-WP0-02、TASK-WP0-04", + "task_ids": [ + "TASK-WP0-02", + "TASK-WP0-04" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "ADMIN-06", + "EXPORT-06", + "NFR-05", + "PROJECT-04" + ], + "acceptance_criteria": [ + "AC-31", + "AC-35", + "AC-44", + "AC-55" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.19" + ], + "requirement_ac_sources": [ + "17.19、AC-55", + "17.19、NFR-05、AC-35、55", + "17.19、NFR-05、AC-55", + "17.19、PROJECT-04、EXPORT-06、AC-55", + "ADMIN-06、17.19、AC-31、55", + "NFR-05、17.19、AC-44、55" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "4.3 文件与数据库一致性", + "4.4 LocalBackendStorageState 实现", + "5.3 支撑表", + "14.7 AC-55" + ], + "modules": [ + "api", + "worker", + "sqlite", + "local-data-root" + ], + "implementation_boundary": "实现 LocalBackendStorageState、精确阈值、storage reservation、staging 流式校验、同卷原子改名、managed file、补偿队列、启动对账和物理删除后重测", + "parent_families": [ + "TDD-WP0-FILE-001", + "TDD-WP0-STO-001", + "TDD-WP0-STO-002", + "TDD-WP0-STO-003" + ], + "case_ids": [ + "TDD-WP0-FILE-001-atomic-commit", + "TDD-WP0-STO-001-byte-thresholds", + "TDD-WP0-STO-002-equal-limit", + "TDD-WP0-STO-002-over-limit", + "TDD-WP0-STO-003-cleanup-remeasure", + "TDD-WP0-STO-003-full-matrix", + "TDD-WP0-STO-003-unavailable-matrix" + ], + "first_red": [ + { + "family_id": "TDD-WP0-FILE-001", + "assertion": "文件流、数据库事务或进程崩溃产生可读半文件、孤儿引用或计量漂移" + }, + { + "family_id": "TDD-WP0-STO-001", + "assertion": "任一阈值使用模糊比例或边界错一字节" + }, + { + "family_id": "TDD-WP0-STO-002", + "assertion": "写入预计量用 \u003e= 拒绝等号,或超限后仍有副作用" + }, + { + "family_id": "TDD-WP0-STO-003", + "assertion": "full/unavailable 动作混淆或清理排队即减字节" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-FILE-001", + "assertion": "预检、staging 流式哈希/MIME、同卷原子改名、SQLite 提交、补偿队列和启动对账全覆盖;路径规范化拒绝 ..、符号链接逃逸和 manifest 外对象" + }, + { + "family_id": "TDD-WP0-STO-001", + "assertion": "第 5.5 节七个精确字节值产生固定 notice/status" + }, + { + "family_id": "TDD-WP0-STO-002", + "assertion": "\u003e 才拒绝;等于上限提交成功后 full;超限不创建任务、冻结、文件关系或上游调用" + }, + { + "family_id": "TDD-WP0-STO-003", + "assertion": "full 允许 JSON 保存与客户端仅下载;unavailable 拒绝新增后端写;物理删除和重测前计量不变" + } + ], + "task_green_gate": "写前只用 \u003e 拒绝,恰好 5 GB 写入成功后 full;超限零副作用;full/unavailable 动作不同;未物理删除前不减字节", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, local-data-root.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "UNIT", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:unit", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-FILE-001-atomic-commit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-001-byte-thresholds/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-002-equal-limit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-002-over-limit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-003-cleanup-remeasure/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-003-full-matrix/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-STO-003-unavailable-matrix/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP0-06", + "work_package": "WP-0", + "title": "公开客户端缓存", + "dependencies": { + "source_text": "TASK-WP0-02、TASK-WP0-04", + "task_ids": [ + "TASK-WP0-02", + "TASK-WP0-04" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-07" + ], + "acceptance_criteria": [ + "AC-46", + "AC-48" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.14" + ], + "requirement_ac_sources": [ + "NFR-07、17.14、AC-46、48" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.4 素材面板与公开缓存", + "10.6 三类资源隔离", + "12.1 本机安全边界" + ], + "modules": [ + "web", + "api", + "client-cache" + ], + "implementation_boundary": "建立 150 MiB public allowlist LRU、Cache Storage/IndexedDB 元数据、写前淘汰和 Service Worker 路由边界", + "parent_families": [ + "TDD-WP0-CACHE-001" + ], + "case_ids": [ + "TDD-WP0-CACHE-001-public-lru" + ], + "first_red": [ + { + "family_id": "TDD-WP0-CACHE-001", + "assertion": "应用缓存超过 157,286,400 字节或含私有/内部内容" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-CACHE-001", + "assertion": "LRU 在写前淘汰;只含 allowlist 公开资源;逐场景枚举 SW、Cache Storage、IndexedDB" + } + ], + "task_green_gate": "逐场景枚举实际浏览器存储;internal/private/项目/提示词/未保存草稿均不进入持久缓存", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web, api, client-cache.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-CACHE-001-public-lru/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP0-07", + "work_package": "WP-0", + "title": "Supervisor、凭据和运行诊断", + "dependencies": { + "source_text": "TASK-WP0-01、TASK-WP0-04、TASK-WP0-05", + "task_ids": [ + "TASK-WP0-01", + "TASK-WP0-04", + "TASK-WP0-05" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-09", + "PRIV-01" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-09、AC-24、41", + "PRIV-01、AC-41" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.1 便携包布局", + "3.2 Supervisor 生命周期", + "3.3 离线配置命令", + "3.7 辅助系统界面契约", + "12.3 审计与敏感信息" + ], + "modules": [ + "supervisor", + "api", + "worker", + "windows-credential-manager" + ], + "implementation_boundary": "实现单实例 mutex、固定端口检查、API/Worker 启停与有限重启、15 秒退出、Credential Manager 匿名管道注入、托盘状态、离线配置命令和脱敏诊断", + "parent_families": [ + "TDD-WP0-SEC-001", + "TDD-WP0-SUP-001" + ], + "case_ids": [ + "TDD-WP0-SEC-001-credential-channel", + "TDD-WP0-SUP-001-lifecycle" + ], + "first_red": [ + { + "family_id": "TDD-WP0-SEC-001", + "assertion": "凭据进入参数、环境、文件、日志或错误" + }, + { + "family_id": "TDD-WP0-SUP-001", + "assertion": "第二实例、端口占用、API/Worker 崩溃或退出留下残留进程" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-SEC-001", + "assertion": "Supervisor 通过继承匿名管道最小传递;API/Worker 分离;缺失、清除、重启和扫描全通过" + }, + { + "family_id": "TDD-WP0-SUP-001", + "assertion": "mutex、固定端口、有限重启、degraded 状态、15 秒优雅退出和托盘允许动作符合 DevelopmentPlan" + } + ], + "task_green_gate": "第二实例、端口占用、startup/api/worker degraded、凭据缺失/清除/重启和子进程残留均有确定行为;不动态换端口、不泄露凭据", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: supervisor, api, worker, windows-credential-manager.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "PKG-SEC", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-SEC-001-credential-channel/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-SUP-001-lifecycle/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "system-ui", + "role": "implementation", + "frames": { + "frame_id": "awt96", + "frame_name": "07B Windows 辅助系统界面", + "primary_task": "TASK-WP0-07", + "mapped_state_scope": "托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作" + }, + "applicable_states": { + "page_id": "system-ui", + "actor_entry": "本机托盘/Supervisor,无产品角色", + "normal": "托盘、运行状态、打开 Dada、诊断、退出", + "empty_loading": "启动中;N/A 业务空态", + "failure_disabled": "启动失败、端口占用、API/Worker degraded,动作严格受限", + "conflict_capacity_special": "full/unavailable 只作脱敏告警;固定端口不动态切换" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/system-ui/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP0-08", + "work_package": "WP-0", + "title": "日志、轮转和不可写联动", + "dependencies": { + "source_text": "TASK-WP0-05、TASK-WP0-07", + "task_ids": [ + "TASK-WP0-05", + "TASK-WP0-07" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-09", + "PRIV-01", + "PRIV-02" + ], + "acceptance_criteria": [ + "AC-41", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "PRIV-01、02、NFR-09、AC-41、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "4.5 日志与数据库维护", + "12.3 审计与敏感信息" + ], + "modules": [ + "api", + "worker", + "supervisor", + "local-data-root" + ], + "implementation_boundary": "建立 API/Worker/Supervisor JSONL 白名单日志、10 MiB/10 文件/30 天轮转、诊断复用脱敏器和日志不可写状态联动", + "parent_families": [ + "TDD-WP0-LOG-001" + ], + "case_ids": [ + "TDD-WP0-LOG-001-rotation-redaction" + ], + "first_red": [ + { + "family_id": "TDD-WP0-LOG-001", + "assertion": "日志无限增长、含敏感数据或目录不可写后仍调用 AI" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-LOG-001", + "assertion": "10 MiB/10 文件/30 天轮转;白名单字段;写失败进入 unavailable 并阻断 AI" + } + ], + "task_green_gate": "日志不含密钥、邮箱、提示词、图片、绝对路径或供应商原文;日志不可写进入 unavailable 并阻断 AI", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, supervisor, local-data-root.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "WRK", + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:worker", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-LOG-001-rotation-redaction/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "system-ui", + "role": "implementation", + "frames": { + "frame_id": "awt96", + "frame_name": "07B Windows 辅助系统界面", + "primary_task": "TASK-WP0-07", + "mapped_state_scope": "托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作" + }, + "applicable_states": { + "page_id": "system-ui", + "actor_entry": "本机托盘/Supervisor,无产品角色", + "normal": "托盘、运行状态、打开 Dada、诊断、退出", + "empty_loading": "启动中;N/A 业务空态", + "failure_disabled": "启动失败、端口占用、API/Worker degraded,动作严格受限", + "conflict_capacity_special": "full/unavailable 只作脱敏告警;固定端口不动态切换" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/system-ui/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP0-09", + "work_package": "WP-0", + "title": "隔离 CI 与便携包", + "dependencies": { + "source_text": "TASK-WP0-01 至 TASK-WP0-08", + "task_ids": [ + "TASK-WP0-01", + "TASK-WP0-08" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-01", + "NFR-09" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-01、09、AC-24、41、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.1 便携包布局", + "3.4 环境关系", + "14.1 测试层级", + "15 WP-0" + ], + "modules": [ + "release-ci", + "supervisor", + "api", + "worker", + "web" + ], + "implementation_boundary": "配置 Windows Gitea Runner 隔离流水线;生成 Dada.exe、随包 Node、API/Worker、迁移、资源元数据、RELEASE.json 占位规则、START-HERE.txt、ZIP 和 SHA-256", + "parent_families": [ + "TDD-WP0-PKG-001" + ], + "case_ids": [ + "TDD-WP0-PKG-001-portable-zip" + ], + "first_red": [ + { + "family_id": "TDD-WP0-PKG-001", + "assertion": "ZIP 缺组件、包含真实配置或动态换端口" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP0-PKG-001", + "assertion": "便携布局、SHA-256、START-HERE、RELEASE 记录和固定端口全部正确,制品零凭据" + } + ], + "task_green_gate": "CI 不读真实凭据、LocalDataRoot 或 3.57 GB 归档;包固定 43121、零真实配置/路径;正式 RELEASE 记录在 WP-7 冻结", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-ci, supervisor, api, worker, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP0-PKG-001-portable-zip/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-0", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "system-ui", + "role": "implementation", + "frames": { + "frame_id": "awt96", + "frame_name": "07B Windows 辅助系统界面", + "primary_task": "TASK-WP0-07", + "mapped_state_scope": "托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作" + }, + "applicable_states": { + "page_id": "system-ui", + "actor_entry": "本机托盘/Supervisor,无产品角色", + "normal": "托盘、运行状态、打开 Dada、诊断、退出", + "empty_loading": "启动中;N/A 业务空态", + "failure_disabled": "启动失败、端口占用、API/Worker degraded,动作严格受限", + "conflict_capacity_special": "full/unavailable 只作脱敏告警;固定端口不动态切换" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/system-ui/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-01", + "work_package": "WP-1", + "title": "认证数据与邀请注册事务", + "dependencies": { + "source_text": "WP-0", + "task_ids": [ + + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "AUTH-01", + "AUTH-02", + "AUTH-03", + "AUTH-04", + "NFR-04" + ], + "acceptance_criteria": [ + "AC-01", + "AC-02", + "AC-45" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "AUTH-01 至 04、AC-01", + "AUTH-01、02、AC-02", + "AUTH-01、NFR-04、AC-01、02、45" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CHALLENGE", + "FX-INVITE-EXPIRED", + "FX-INVITE-VALID" + ], + "development_plan_sections": [ + "5.2 十九个产品数据契约映射", + "6.2 API 分组", + "7.3 生成提交事务", + "9.1 邀请注册、验证码、会话和管理员" + ], + "modules": [ + "api", + "sqlite", + "resend-adapter", + "shared-contracts" + ], + "implementation_boundary": "建立 UserProfile、邀请码、验证码挑战、普通账号初始点数/registration_grant、幂等键与事务;接入 Resend mock", + "parent_families": [ + "TDD-WP1-AUTH-001", + "TDD-WP1-AUTH-002" + ], + "case_ids": [ + "TDD-WP1-AUTH-001-final-recheck-race", + "TDD-WP1-AUTH-001-valid-register", + "TDD-WP1-AUTH-002-invalid-invite" + ], + "first_red": [ + { + "family_id": "TDD-WP1-AUTH-001", + "assertion": "有效邀请注册任一步部分成功" + }, + { + "family_id": "TDD-WP1-AUTH-002", + "assertion": "无效/过期/停用/耗尽邀请码仍发码或占用次数" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-AUTH-001", + "assertion": "发码前和建号前复核;账号、初始 10 点、registration_grant、告知版本和邀请码次数同事务一次提交" + }, + { + "family_id": "TDD-WP1-AUTH-002", + "assertion": "不发码、不建号、不赠点,返回稳定原因且无副作用" + } + ], + "task_green_gate": "发码前/建号前复核;有效注册一次提交;无效、过期、停用、耗尽邀请零副作用", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, resend-adapter, shared-contracts.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-001-final-recheck-race/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-001-valid-register/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-002-invalid-invite/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-invites", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "创建/停用/期限/次数/脱敏复制、空/骨架、名额满、过期/耗尽、请求失败和幂等/CAS 结果" + }, + "applicable_states": { + "page_id": "admin-invites", + "actor_entry": "active super_admin", + "normal": "创建、停用、期限/次数、脱敏复制", + "empty_loading": "无邀请码;表格骨架", + "failure_disabled": "名额满、停用/过期/耗尽不可当可用;请求失败", + "conflict_capacity_special": "CAS/幂等结果;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-invites/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-02", + "work_package": "WP-1", + "title": "登录状态机、会话与登录画面", + "dependencies": { + "source_text": "TASK-WP1-01", + "task_ids": [ + "TASK-WP1-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "AUTH-01", + "AUTH-02", + "AUTH-04", + "AUTH-05", + "AUTH-06", + "AUTH-07" + ], + "acceptance_criteria": [ + "AC-22", + "AC-33", + "AC-49" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "AUTH-01、02、05、06、AC-33", + "AUTH-02、04、AC-33", + "AUTH-04 至 07、AC-22、33、49" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CHALLENGE" + ], + "development_plan_sections": [ + "6.2 API 分组", + "6.3 鉴权与会话", + "9.1 邀请注册、验证码、会话和管理员" + ], + "modules": [ + "api", + "sqlite", + "web" + ], + "implementation_boundary": "实现注册/登录分离入口、未注册/已注册/已注销/suspended 状态、10 分钟验证码、60 秒重发、30 天 opaque session、CSRF 和退出/暂停/注销撤销;按 Z0pf8 落实普通用户登录布局、艺术带、管理员文字入口、固定本机提示和焦点", + "parent_families": [ + "TDD-WP1-AUTH-003", + "TDD-WP1-AUTH-004" + ], + "case_ids": [ + "TDD-WP1-AUTH-003-entry-state-matrix", + "TDD-WP1-AUTH-004-challenge-guards", + "TDD-WP1-AUTH-004-session-revocation" + ], + "first_red": [ + { + "family_id": "TDD-WP1-AUTH-003", + "assertion": "注册/登录对未注册、已注册、已注销、suspended 邮箱静默切换流程" + }, + { + "family_id": "TDD-WP1-AUTH-004", + "assertion": "验证码重用、过期仍有效或会话未及时撤销" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-AUTH-003", + "assertion": "六种状态严格按 PRD 分离入口;已注销邮箱只能新邀请重建且旧数据不恢复" + }, + { + "family_id": "TDD-WP1-AUTH-004", + "assertion": "10 分钟一次性、60 秒重发、30 天会话;退出/暂停/注销/白名单移除立即撤销" + } + ], + "task_green_gate": "六类身份状态不静默换流程;验证码不可复用;会话撤销即时生效;普通与管理员 audience 分离;登录页状态与布局证据齐全", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "API", + "E2E", + "DB" + ], + "validation_commands": [ + "pnpm test:api", + "pnpm test:e2e", + "pnpm test:integration", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-003-entry-state-matrix/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-004-challenge-guards/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUTH-004-session-revocation/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "user-auth", + "role": "implementation", + "frames": [ + { + "frame_id": "Z0pf8", + "frame_name": "01A 普通用户登录", + "primary_task": "TASK-WP1-02", + "mapped_state_scope": "登录正常;字段初始空;发码/登录加载;验证码、Resend 和身份状态失败;管理员入口、固定本机提示、艺术带与焦点" + }, + { + "frame_id": "DVPM8", + "frame_name": "01B 注册展开态", + "primary_task": "TASK-WP1-03", + "mapped_state_scope": "邀请与邮箱已验证摘要;资料和告知未完成/完成;注册中;名额、邀请、验证码和提交失败;主动作禁用" + } + ], + "applicable_states": { + "page_id": "user-auth", + "actor_entry": "未登录普通入口", + "normal": "登录、分阶段注册、管理员文字入口、固定本机提示", + "empty_loading": "字段初始空;发码/验证/注册加载", + "failure_disabled": "Resend、验证码、邀请、名额、告知未完成;按钮禁用", + "conflict_capacity_special": "N/A:认证不消费存储容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/user-auth/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-03", + "work_package": "WP-1", + "title": "注册展开态、内测告知与账号名额", + "dependencies": { + "source_text": "TASK-WP1-01、TASK-WP1-02", + "task_ids": [ + "TASK-WP1-01", + "TASK-WP1-02" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "AUTH-01", + "AUTH-03", + "NFR-04", + "PRIV-04", + "PRIV-06" + ], + "acceptance_criteria": [ + "AC-01", + "AC-32", + "AC-41", + "AC-45", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "AUTH-01、NFR-04、AC-32、45", + "AUTH-03、PRIV-04、06、AC-01、41、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "6.3 鉴权与会话", + "9.1 邀请注册、验证码、会话和管理员", + "12.1 本机安全边界" + ], + "modules": [ + "api", + "sqlite", + "web" + ], + "implementation_boundary": "实现版本化内测告知、必选确认、资料字段、active+suspended 普通账号 10 人上限、deleted 释放和 super_admin 不占名额;按 DVPM8 落实邀请/邮箱只读摘要、资料、告知和注册主动作", + "parent_families": [ + "TDD-WP1-NOTICE-001", + "TDD-WP1-SLOT-001" + ], + "case_ids": [ + "TDD-WP1-NOTICE-001-registration-consent", + "TDD-WP1-SLOT-001-stage-limit" + ], + "first_red": [ + { + "family_id": "TDD-WP1-NOTICE-001", + "assertion": "未勾选、版本缺失或告知遗漏第三方/本机数据处理仍可注册" + }, + { + "family_id": "TDD-WP1-SLOT-001", + "assertion": "suspended 不占名额、super_admin 占名额或第 11 人可注册" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-NOTICE-001", + "assertion": "告知含版本和生效日期,覆盖 AI 网关、Resend、高德、本机存储与无备份、管理员访问、DYN004、注销和 180 天规则;未同意不建号,成功时记录版本/时间" + }, + { + "family_id": "TDD-WP1-SLOT-001", + "assertion": "active+suspended 普通账号最多 10;deleted 释放;super_admin 不占且不用邀请码" + } + ], + "task_green_gate": "告知覆盖 PRD 固定事项;未同意不建号;第 11 个普通账号被阻断;多个同权限 super_admin 合法;注册展开态状态与布局证据齐全", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "API", + "E2E", + "MANUAL", + "DB" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:api", + "pnpm test:e2e", + "pnpm test:integration", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-NOTICE-001-registration-consent/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-SLOT-001-stage-limit/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "user-auth", + "role": "implementation", + "frames": [ + { + "frame_id": "Z0pf8", + "frame_name": "01A 普通用户登录", + "primary_task": "TASK-WP1-02", + "mapped_state_scope": "登录正常;字段初始空;发码/登录加载;验证码、Resend 和身份状态失败;管理员入口、固定本机提示、艺术带与焦点" + }, + { + "frame_id": "DVPM8", + "frame_name": "01B 注册展开态", + "primary_task": "TASK-WP1-03", + "mapped_state_scope": "邀请与邮箱已验证摘要;资料和告知未完成/完成;注册中;名额、邀请、验证码和提交失败;主动作禁用" + } + ], + "applicable_states": { + "page_id": "user-auth", + "actor_entry": "未登录普通入口", + "normal": "登录、分阶段注册、管理员文字入口、固定本机提示", + "empty_loading": "字段初始空;发码/验证/注册加载", + "failure_disabled": "Resend、验证码、邀请、名额、告知未完成;按钮禁用", + "conflict_capacity_special": "N/A:认证不消费存储容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/user-auth/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-04", + "work_package": "WP-1", + "title": "管理员白名单与安全配置生效", + "dependencies": { + "source_text": "WP-0、TASK-WP1-01、TASK-WP1-02", + "task_ids": [ + "TASK-WP1-01", + "TASK-WP1-02" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "ADMIN-09", + "AUTH-07" + ], + "acceptance_criteria": [ + "AC-49", + "AC-50" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "AUTH-07、AC-49", + "AUTH-07、ADMIN-09、AC-49、50" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.3 离线配置命令", + "6.3 鉴权与会话", + "9.1 邀请注册、验证码、会话和管理员" + ], + "modules": [ + "supervisor", + "api", + "sqlite", + "windows-credential-manager", + "web-admin-auth" + ], + "implementation_boundary": "实现 HMAC 管理员白名单、独立管理员认证、多管理员、安全配置 revision、原子应用/回滚、system 审计和离线恢复边界", + "parent_families": [ + "TDD-WP1-ADM-001", + "TDD-WP1-CFG-001" + ], + "case_ids": [ + "TDD-WP1-ADM-001-admin-auth-boundary", + "TDD-WP1-CFG-001-secure-revision" + ], + "first_red": [ + { + "family_id": "TDD-WP1-ADM-001", + "assertion": "白名单外发码、普通账号提升或全部管理员停用后自助恢复" + }, + { + "family_id": "TDD-WP1-CFG-001", + "assertion": "安全配置修订部分生效或泄露白名单/HMAC" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-ADM-001", + "assertion": "后台独立 audience;多管理员可用;恢复仅安全配置;部署人员无产品身份" + }, + { + "family_id": "TDD-WP1-CFG-001", + "assertion": "完整验证后单一事务生效、撤销会话、system 审计和 revision 推进;失败整体回滚" + } + ], + "task_green_gate": "非白名单发码前拒绝;普通账号不能原地提升;配置失败不部分生效;部署人员不成为产品角色或审计 actor", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: supervisor, api, sqlite, windows-credential-manager, web-admin-auth.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E", + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-ADM-001-admin-auth-boundary/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-CFG-001-secure-revision/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-auth", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP1-04", + "mapped_state_scope": "白名单邮箱验证码、字段空/加载、非白名单、停用/移除、Resend 失败;独立 audience 与返回普通登录" + }, + "applicable_states": { + "page_id": "admin-auth", + "actor_entry": "未登录后台入口", + "normal": "白名单邮箱验证码、独立 audience、返回普通登录", + "empty_loading": "字段空;发码/登录中", + "failure_disabled": "非白名单发码前阻断、停用/移除白名单、Resend 错误", + "conflict_capacity_special": "N/A:不显示普通创作或容量业务" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-auth/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-05", + "work_package": "WP-1", + "title": "注销、匿名保留与物理清理", + "dependencies": { + "source_text": "TASK-WP1-01 至 TASK-WP1-04", + "task_ids": [ + "TASK-WP1-01", + "TASK-WP1-04" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "AUTH-06" + ], + "acceptance_criteria": [ + "AC-22", + "AC-39", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.10", + "17.11" + ], + "requirement_ac_sources": [ + "AUTH-06、17.10、17.11、AC-22、39", + "AUTH-06、AC-22、39、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.2 项目、历史、失败草稿和回收站", + "12.2 账号注销" + ], + "modules": [ + "api", + "worker", + "sqlite", + "local-data-root", + "web" + ], + "implementation_boundary": "实现注销事务、立即撤权与断链、邮箱释放、匿名事件、物理清理队列和最长 180 天保留", + "parent_families": [ + "TDD-WP1-DEL-001", + "TDD-WP1-DEL-002" + ], + "case_ids": [ + "TDD-WP1-DEL-001-account-delete", + "TDD-WP1-DEL-002-anonymous-retention" + ], + "first_red": [ + { + "family_id": "TDD-WP1-DEL-001", + "assertion": "注销后仍可访问账号、项目、资源、坐标或会话" + }, + { + "family_id": "TDD-WP1-DEL-002", + "assertion": "匿名事件可回溯或注销延长访问审计期限" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-DEL-001", + "assertion": "注销事务立即撤权和断链,物理内容异步清理,邮箱可作为全新账号重新注册" + }, + { + "family_id": "TDD-WP1-DEL-002", + "assertion": "只保留允许字段和随机主体;匿名事件不晚于注销后 180 天;访问审计沿原 expires_at" + } + ], + "task_green_gate": "旧数据不可恢复;原邮箱只能新邀请新建;匿名事件不可反查;注销不延长私有访问审计期限", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, local-data-root, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-DEL-001-account-delete/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-DEL-002-anonymous-retention/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "settings", + "role": "implementation", + "frames": { + "frame_id": "Ws5E0", + "frame_name": "05B 设置与本机数据", + "primary_task": "TASK-WP1-05", + "mapped_state_scope": "资料、本机数据、容量摘要和固定文案;加载/保存失败;full/unavailable;注销确认、处理中和返回登录" + }, + "applicable_states": { + "page_id": "settings", + "actor_entry": "active 普通/内部预览账号", + "normal": "资料、本机数据、容量摘要、固定文案、注销", + "empty_loading": "资料/状态加载", + "failure_disabled": "保存失败;注销确认/处理中禁用重复提交", + "conflict_capacity_special": "full/unavailable 状态与允许动作;注销后返回登录" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/settings/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP1-06", + "work_package": "WP-1", + "title": "两类审计基础不可变性", + "dependencies": { + "source_text": "WP-0、TASK-WP1-04、TASK-WP1-05", + "task_ids": [ + "TASK-WP1-04", + "TASK-WP1-05" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "ADMIN-05", + "ADMIN-09" + ], + "acceptance_criteria": [ + "AC-50" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.10", + "17.16" + ], + "requirement_ac_sources": [ + "ADMIN-05、09、17.10、17.16、AC-50" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "5.2 十九个产品数据契约映射", + "5.3 支撑表", + "9.6 运营后台与两类审计", + "12.3 审计与敏感信息" + ], + "modules": [ + "api", + "worker", + "sqlite" + ], + "implementation_boundary": "建立 AdminOperationLog 与 PrivateContentAccessLog 表、append-only trigger、合法 retention/privacy purge 作用域和非敏感摘要", + "parent_families": [ + "TDD-WP1-AUD-001" + ], + "case_ids": [ + "TDD-WP1-AUD-001-immutable-triggers" + ], + "first_red": [ + { + "family_id": "TDD-WP1-AUD-001", + "assertion": "普通连接可改删两类审计" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP1-AUD-001", + "assertion": "trigger 阻断 UPDATE/DELETE;仅保留期/隐私清理作用域可执行规定删除" + } + ], + "task_green_gate": "普通连接不能改删;两类日志不混用;正文、图片、密钥和路径不进入审计", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "SQLite constraints, transactions, idempotency and side-effect assertions" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP1-AUD-001-immutable-triggers/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-1", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP2-01", + "work_package": "WP-2", + "title": "项目、历史、失败草稿与空工作台", + "dependencies": { + "source_text": "WP-0、WP-1", + "task_ids": [ + + ], + "work_packages": [ + "WP-0", + "WP-1" + ] + }, + "requirements": [ + "GEN-04", + "GEN-09", + "GEN-11", + "PROJECT-01", + "PROJECT-02", + "PROJECT-03" + ], + "acceptance_criteria": [ + "AC-03", + "AC-04", + "AC-06", + "AC-34", + "AC-43" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "PROJECT-01 至 03、GEN-04、11、AC-03、06、34", + "PROJECT-01、GEN-09、AC-04、43" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-PROJECT-ACTIVE-19", + "FX-PROJECT-EMPTY" + ], + "development_plan_sections": [ + "6.2 API 分组", + "9.2 项目、历史、失败草稿和回收站" + ], + "modules": [ + "api", + "sqlite", + "web" + ], + "implementation_boundary": "实现新建项目、项目内追加、固定比例、20 active、10 成功图、默认命名/重命名、failed_empty 重试和批量入回收站;按 WbDYT 落实无项目工作台艺术带、生成表单和空任务区", + "parent_families": [ + "TDD-WP2-PROJ-001", + "TDD-WP2-PROJ-005" + ], + "case_ids": [ + "TDD-WP2-PROJ-001-new-versus-continue", + "TDD-WP2-PROJ-005-failed-draft-retry" + ], + "first_red": [ + { + "family_id": "TDD-WP2-PROJ-001", + "assertion": "新建/继续生成/历史上限混淆项目数量或比例" + }, + { + "family_id": "TDD-WP2-PROJ-005", + "assertion": "重试新建项目或成功项目混入 failed_empty 批量删除" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-PROJ-001", + "assertion": "每次“新建创作”新项目;继续生成只追加;比例固定;10 张历史阻断追加但不阻断新项目" + }, + { + "family_id": "TDD-WP2-PROJ-005", + "assertion": "同一草稿每次重试只新增任务;仅无成功图草稿可批量入回收站" + } + ], + "task_green_gate": "新建/追加/上限不混淆;重试只新建任务;有成功图项目不能进入 failed_empty 批量操作;艺术带仅在无项目工作台出现", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-001-new-versus-continue/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-005-failed-draft-retry/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "workspace", + "role": "implementation", + "frames": [ + { + "frame_id": "WbDYT", + "frame_name": "02A 空工作台", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "无项目艺术带;生成表单、推荐模型、四比例与空任务;模型/项目加载;模型、点数、参考图和容量禁用" + }, + { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + } + ], + "applicable_states": { + "page_id": "workspace", + "actor_entry": "active 普通/内部预览账号", + "normal": "生成表单、模型/点数/比例、最近项目", + "empty_loading": "无项目艺术带;模型/项目加载", + "failure_disabled": "服务/参考图/生成错误;模型、点数、单任务、历史/项目上限禁用", + "conflict_capacity_special": "model config 变化重确认;warning/critical 提示;full/unavailable 阻断新增" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/workspace/\u003cstate\u003e/" + }, + { + "page_id": "projects", + "role": "implementation", + "frames": { + "frame_id": "zXyG9", + "frame_name": "03A 项目列表", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "active/trashed、筛选、多选 failed_empty;空/骨架;20 active、恢复/批量禁用;多标签和容量提示" + }, + "applicable_states": { + "page_id": "projects", + "actor_entry": "active 普通/内部预览账号", + "normal": "active/trashed 列表、筛选、多选 failed_empty", + "empty_loading": "无项目/筛选空;稳定卡片骨架", + "failure_disabled": "列表失败保留旧数据;20 active、恢复/批量条件禁用", + "conflict_capacity_special": "多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/projects/\u003cstate\u003e/" + }, + { + "page_id": "project-detail", + "role": "implementation", + "frames": { + "frame_id": "v2Qbu", + "frame_name": "03B 项目详情", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "历史、固定比例、继续生成、编辑、原图/latest;无成功图/无成品;10 图上限、资源失败和容量状态" + }, + "applicable_states": { + "page_id": "project-detail", + "actor_entry": "项目所有者", + "normal": "历史、固定比例、继续生成、编辑、原图/latest 下载", + "empty_loading": "无成功图草稿;历史/成品加载", + "failure_disabled": "10 图上限、当前底图不可删、无 latest、资源读取失败", + "conflict_capacity_special": "比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/project-detail/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-02", + "work_package": "WP-2", + "title": "自动保存与多标签冲突", + "dependencies": { + "source_text": "TASK-WP2-01", + "task_ids": [ + "TASK-WP2-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "EXPORT-05", + "EXPORT-06", + "PROJECT-04", + "PROJECT-05", + "PROJECT-06", + "PROJECT-07" + ], + "acceptance_criteria": [ + "AC-20", + "AC-35", + "AC-36" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.3" + ], + "requirement_ac_sources": [ + "PROJECT-04、05、EXPORT-06、AC-20、35", + "PROJECT-04、06、AC-20", + "PROJECT-04、17.3、AC-20", + "PROJECT-07、AC-36 P0-A 子集", + "PROJECT-07、EXPORT-05、AC-36 P0-A 子集" + ], + "acceptance_scope_notes": [ + "PROJECT-07、AC-36 P0-A 子集", + "PROJECT-07、EXPORT-05、AC-36 P0-A 子集" + ], + "fixture_ids": [ + "FX-PROJECT-CONFLICT" + ], + "development_plan_sections": [ + "7.5 项目保存并发", + "9.2 项目、历史、失败草稿和回收站" + ], + "modules": [ + "web", + "api", + "sqlite", + "shared-contracts" + ], + "implementation_boundary": "实现 Dada Canvas Schema、1 秒 debounce、单请求在途、最新快照合并、If-Match CAS、dirty/saving/saved/failed/conflicted、离开守卫和一次冲突本地导出", + "parent_families": [ + "TDD-WP2-PROJ-002", + "TDD-WP2-PROJ-004" + ], + "case_ids": [ + "TDD-WP2-PROJ-002-cas-success", + "TDD-WP2-PROJ-002-debounce", + "TDD-WP2-PROJ-002-save-failure-recovery", + "TDD-WP2-PROJ-004-conflict-export-once", + "TDD-WP2-PROJ-004-stale-tab" + ], + "first_red": [ + { + "family_id": "TDD-WP2-PROJ-002", + "assertion": "连续编辑并发保存、失败显示已保存或刷新恢复私有草稿" + }, + { + "family_id": "TDD-WP2-PROJ-004", + "assertion": "旧标签覆盖、另存副本或多次冲突导出" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-PROJ-002", + "assertion": "1 秒 debounce、单请求在途、最新快照合并;失败重试;强制退出只恢复 saved,撤销栈为空" + }, + { + "family_id": "TDD-WP2-PROJ-004", + "assertion": "CAS 返回 412;旧页只读;仅一次本地导出且不写 latest;刷新后端最新状态" + } + ], + "task_green_gate": "旧标签不能覆盖或另存副本;冲突页只读且仅一次本地导出,不写 latest;刷新只恢复 saved", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web, api, sqlite, shared-contracts.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-002-cas-success/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-002-debounce/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-002-save-failure-recovery/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-004-conflict-export-once/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-004-stale-tab/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "projects", + "role": "implementation", + "frames": { + "frame_id": "zXyG9", + "frame_name": "03A 项目列表", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "active/trashed、筛选、多选 failed_empty;空/骨架;20 active、恢复/批量禁用;多标签和容量提示" + }, + "applicable_states": { + "page_id": "projects", + "actor_entry": "active 普通/内部预览账号", + "normal": "active/trashed 列表、筛选、多选 failed_empty", + "empty_loading": "无项目/筛选空;稳定卡片骨架", + "failure_disabled": "列表失败保留旧数据;20 active、恢复/批量条件禁用", + "conflict_capacity_special": "多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/projects/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-03", + "work_package": "WP-2", + "title": "回收站、恢复与 purge", + "dependencies": { + "source_text": "TASK-WP2-01、WP-0 文件清理", + "task_ids": [ + "TASK-WP2-01" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "PROJECT-03", + "PROJECT-08" + ], + "acceptance_criteria": [ + "AC-21" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "PROJECT-03、08、AC-21" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-PROJECT-ACTIVE-20" + ], + "development_plan_sections": [ + "9.2 项目、历史、失败草稿和回收站", + "12.2 账号注销" + ], + "modules": [ + "api", + "worker", + "sqlite", + "local-data-root", + "web" + ], + "implementation_boundary": "实现 trashed、精确 +720h purge_at、20 active 恢复限制、立即撤权和异步物理清理", + "parent_families": [ + "TDD-WP2-PROJ-003" + ], + "case_ids": [ + "TDD-WP2-PROJ-003-trash-restore-purge" + ], + "first_red": [ + { + "family_id": "TDD-WP2-PROJ-003", + "assertion": "回收站计时、20 项目恢复或 purge 可恢复边界错误" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-PROJ-003", + "assertion": "purge_at 精确 +720 小时;满 20 保持 trashed;purged 立即不可访问并异步清理" + } + ], + "task_green_gate": "满 20 时保持 trashed;purged 后立即不可读;删除排队不伪造容量下降", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, local-data-root, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-PROJ-003-trash-restore-purge/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "projects", + "role": "implementation", + "frames": { + "frame_id": "zXyG9", + "frame_name": "03A 项目列表", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "active/trashed、筛选、多选 failed_empty;空/骨架;20 active、恢复/批量禁用;多标签和容量提示" + }, + "applicable_states": { + "page_id": "projects", + "actor_entry": "active 普通/内部预览账号", + "normal": "active/trashed 列表、筛选、多选 failed_empty", + "empty_loading": "无项目/筛选空;稳定卡片骨架", + "failure_disabled": "列表失败保留旧数据;20 active、恢复/批量条件禁用", + "conflict_capacity_special": "多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/projects/\u003cstate\u003e/" + }, + { + "page_id": "project-detail", + "role": "implementation", + "frames": { + "frame_id": "v2Qbu", + "frame_name": "03B 项目详情", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "历史、固定比例、继续生成、编辑、原图/latest;无成功图/无成品;10 图上限、资源失败和容量状态" + }, + "applicable_states": { + "page_id": "project-detail", + "actor_entry": "项目所有者", + "normal": "历史、固定比例、继续生成、编辑、原图/latest 下载", + "empty_loading": "无成功图草稿;历史/成品加载", + "failure_disabled": "10 图上限、当前底图不可删、无 latest、资源读取失败", + "conflict_capacity_special": "比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/project-detail/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-04", + "work_package": "WP-2", + "title": "点数账户、流水与管理员调点", + "dependencies": { + "source_text": "WP-1、WP-0 SQLite", + "task_ids": [ + + ], + "work_packages": [ + "WP-0", + "WP-1" + ] + }, + "requirements": [ + "ADMIN-01", + "ADMIN-09", + "CREDIT-01", + "CREDIT-02", + "CREDIT-03", + "CREDIT-04", + "CREDIT-05", + "CREDIT-06", + "GEN-09", + "GEN-10" + ], + "acceptance_criteria": [ + "AC-03", + "AC-04", + "AC-28", + "AC-29", + "AC-50" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "CREDIT-01 至 05、AC-03", + "CREDIT-03、05、GEN-09、10、AC-03、04、29", + "CREDIT-04、AC-28", + "CREDIT-05、06、ADMIN-01、09、AC-29、50" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-ACTOR-ADMIN-A", + "FX-CREDIT-ENOUGH", + "FX-CREDIT-INSUFFICIENT", + "FX-JOB-ACTIVE", + "FX-MODEL-VERIFIED" + ], + "development_plan_sections": [ + "7.3 生成提交事务", + "7.4 结算事务", + "9.6 运营后台与两类审计" + ], + "modules": [ + "api", + "sqlite", + "web-admin" + ], + "implementation_boundary": "实现 available/reserved、reserve/commit/release、不可变 ledger、operation_key、余额不足和带原因的 admin adjustment", + "parent_families": [ + "TDD-WP2-CRED-001", + "TDD-WP2-CRED-002" + ], + "case_ids": [ + "TDD-WP2-CRED-001-finalize-once", + "TDD-WP2-CRED-001-insufficient", + "TDD-WP2-CRED-001-reserve", + "TDD-WP2-CRED-002-admin-adjustment" + ], + "first_red": [ + { + "family_id": "TDD-WP2-CRED-001", + "assertion": "点数不足、重复结算或生成流程制造负数" + }, + { + "family_id": "TDD-WP2-CRED-002", + "assertion": "管理员调点修改 reserved 或缺原因/审计" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-CRED-001", + "assertion": "不足不建任务/流水/上游;reserve/commit/release 各一次;生成流程 available 不为负" + }, + { + "family_id": "TDD-WP2-CRED-002", + "assertion": "只改 available,可为负;原因、流水和 AdminOperationLog 同事务" + } + ], + "task_green_gate": "生成流程 available 不为负;结算一次;调点只改 available、可为负,reserved 不变,流水和审计同事务", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web-admin.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-CRED-001-finalize-once/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-CRED-001-insufficient/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-CRED-001-reserve/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-CRED-002-admin-adjustment/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "credits", + "role": "implementation", + "frames": { + "frame_id": "YlKzE", + "frame_name": "05A 点数明细", + "primary_task": "TASK-WP2-04", + "mapped_state_scope": "available/reserved、流水和分页;无流水/加载;失败保留安全摘要;负 available 如实显示且无购买入口" + }, + "applicable_states": { + "page_id": "credits", + "actor_entry": "active 普通/内部预览账号", + "normal": "available/reserved、流水、关联与时间", + "empty_loading": "无流水;分页加载", + "failure_disabled": "加载失败保留安全余额摘要", + "conflict_capacity_special": "负 available 如实显示;无购买/充值入口;页面操作不消费容量但仍继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/credits/\u003cstate\u003e/" + }, + { + "page_id": "admin-users", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "脱敏列表/详情、暂停恢复/调点、空/筛选空/骨架、原因缺失和状态禁用、负 available" + }, + "applicable_states": { + "page_id": "admin-users", + "actor_entry": "active super_admin", + "normal": "脱敏列表/详情、暂停恢复、调点", + "empty_loading": "无用户/筛选空;表格骨架", + "failure_disabled": "操作失败、原因缺失、状态前置禁用", + "conflict_capacity_special": "调点可使 available 为负但 reserved 不变;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-users/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-05", + "work_package": "WP-2", + "title": "生成提交、单任务、参考图与生成中工作台", + "dependencies": { + "source_text": "TASK-WP2-01、TASK-WP2-04、WP-0 存储", + "task_ids": [ + "TASK-WP2-01", + "TASK-WP2-04" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "ADMIN-03", + "CREDIT-03", + "GEN-01", + "GEN-02", + "GEN-03", + "GEN-04", + "GEN-05", + "GEN-06", + "GEN-07", + "GEN-08", + "GEN-15", + "PROJECT-01", + "PROJECT-09" + ], + "acceptance_criteria": [ + "AC-03", + "AC-05", + "AC-29", + "AC-30", + "AC-34", + "AC-44", + "AC-48" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "CREDIT-03、GEN-07、AC-05、29", + "GEN-01 至 08、PROJECT-01、AC-03、34、44", + "GEN-05、PROJECT-09、AC-44、48", + "GEN-15、ADMIN-03、AC-30" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CREDIT-ENOUGH", + "FX-MODEL-VERIFIED", + "FX-REF-VALID" + ], + "development_plan_sections": [ + "6.2 API 分组", + "7.1 幂等规则", + "7.2 单任务约束", + "7.3 生成提交事务", + "9.2 项目、历史、失败草稿和回收站" + ], + "modules": [ + "api", + "sqlite", + "local-data-root", + "web", + "worker" + ], + "implementation_boundary": "实现 multipart 提交、config/cost 复核、BEGIN IMMEDIATE、活动任务部分唯一索引、参考图内存态/流式提交/关系快照、幂等和 412 重确认;按 d99Lo 落实生成中工作台、当前任务、冻结点数、critical 容量条和禁用提交", + "parent_families": [ + "TDD-WP2-GEN-001", + "TDD-WP2-GEN-003", + "TDD-WP2-REF-001" + ], + "case_ids": [ + "TDD-WP2-GEN-001-concurrent-reserve", + "TDD-WP2-GEN-001-submit-snapshot", + "TDD-WP2-GEN-003-stale-config", + "TDD-WP2-REF-001-reference-lifecycle" + ], + "first_red": [ + { + "family_id": "TDD-WP2-GEN-001", + "assertion": "并发提交创建两个任务、项目、参考关系或冻结" + }, + { + "family_id": "TDD-WP2-GEN-003", + "assertion": "旧 model_config_version 静默按新成本或限制提交" + }, + { + "family_id": "TDD-WP2-REF-001", + "assertion": "未提交参考图被持久化或重试覆盖旧关系" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-GEN-001", + "assertion": "BEGIN IMMEDIATE+部分唯一索引只保留一个 queued/running;后续返回当前任务" + }, + { + "family_id": "TDD-WP2-GEN-003", + "assertion": "返回 412、最新摘要;不建任务、不上传引用、不冻结、不调用上游;重新确认后才提交" + }, + { + "family_id": "TDD-WP2-REF-001", + "assertion": "提交前只在内存;每个任务保存独立关系快照;purged 后立即不可读并异步删除" + } + ], + "task_green_gate": "同账号只建一个活动任务;第二提交返回当前任务且不建项目/关系/冻结;过期配置和无效参考图零持久副作用;生成中页面不显示假进度且提交动作按状态禁用", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, local-data-root, web, worker.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-GEN-001-concurrent-reserve/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-GEN-001-submit-snapshot/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-GEN-003-stale-config/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-REF-001-reference-lifecycle/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "workspace", + "role": "implementation", + "frames": [ + { + "frame_id": "WbDYT", + "frame_name": "02A 空工作台", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "无项目艺术带;生成表单、推荐模型、四比例与空任务;模型/项目加载;模型、点数、参考图和容量禁用" + }, + { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + } + ], + "applicable_states": { + "page_id": "workspace", + "actor_entry": "active 普通/内部预览账号", + "normal": "生成表单、模型/点数/比例、最近项目", + "empty_loading": "无项目艺术带;模型/项目加载", + "failure_disabled": "服务/参考图/生成错误;模型、点数、单任务、历史/项目上限禁用", + "conflict_capacity_special": "model config 变化重确认;warning/critical 提示;full/unavailable 阻断新增" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/workspace/\u003cstate\u003e/" + }, + { + "page_id": "current-task", + "role": "implementation", + "frames": { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + }, + "applicable_states": { + "page_id": "current-task", + "actor_entry": "active 普通/内部预览账号", + "normal": "真实状态、结果入口", + "empty_loading": "无活动任务;queued/running 无假百分比", + "failure_disabled": "failed/rejected 与九类固定动作;第二任务禁用", + "conflict_capacity_special": "storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/current-task/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-06", + "work_package": "WP-2", + "title": "任务终态、九类错误与网关余额影响", + "dependencies": { + "source_text": "TASK-WP2-04、TASK-WP2-05;WP-3 Adapter 接口 mock", + "task_ids": [ + "TASK-WP2-04", + "TASK-WP2-05" + ], + "work_packages": [ + "WP-3" + ] + }, + "requirements": [ + "CREDIT-03", + "GEN-05", + "GEN-06", + "GEN-07", + "GEN-08", + "GEN-09", + "GEN-10", + "GEN-11", + "GEN-13", + "GEN-14", + "GEN-15", + "GEN-16" + ], + "acceptance_criteria": [ + "AC-03", + "AC-04", + "AC-30", + "AC-40", + "AC-44", + "AC-51", + "AC-53" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.17", + "17.18" + ], + "requirement_ac_sources": [ + "GEN-05、16、17.18、AC-04、44、53", + "GEN-06、08、11、CREDIT-03、AC-03", + "GEN-07、16、17.18、AC-30、53", + "GEN-09、16、17.18、AC-04、53", + "GEN-10、16、17.18、AC-04、53", + "GEN-13、16、17.18、AC-40、53", + "GEN-14、15、17.17、AC-04、51", + "GEN-15、16、17.17、17.18、AC-40、51、53" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-GATEWAY-SYNC" + ], + "development_plan_sections": [ + "7.4 结算事务", + "8.5 九类错误统一路径", + "11.3 AI 网关余额" + ], + "modules": [ + "api", + "worker", + "sqlite", + "ai-adapter-contract", + "web" + ], + "implementation_boundary": "实现成功/失败/rejected、输出落盘与一次结算、固定九类错误、用户动作、GatewayBalanceState model/account/unknown 影响和 pending_manual_review", + "parent_families": [ + "TDD-WP2-BAL-001", + "TDD-WP2-ERR-001", + "TDD-WP2-GEN-002" + ], + "case_ids": [ + "TDD-WP2-BAL-001-balance-impact", + "TDD-WP2-ERR-001-gateway-balance", + "TDD-WP2-ERR-001-gateway-contract", + "TDD-WP2-ERR-001-model-disabled", + "TDD-WP2-ERR-001-reference-invalid", + "TDD-WP2-ERR-001-safety-rejected", + "TDD-WP2-ERR-001-unknown-non-retryable", + "TDD-WP2-ERR-001-unknown-retryable", + "TDD-WP2-ERR-001-upstream-failed", + "TDD-WP2-ERR-001-upstream-timeout", + "TDD-WP2-GEN-002-success" + ], + "first_red": [ + { + "family_id": "TDD-WP2-BAL-001", + "assertion": "模型/账户/未知余额影响改写配置或重复释放" + }, + { + "family_id": "TDD-WP2-ERR-001", + "assertion": "九类错误被新增、归并、泄露原文或错误处理点数" + }, + { + "family_id": "TDD-WP2-GEN-002", + "assertion": "成功/失败/拒绝与点数、项目历史或重试不一致" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-BAL-001", + "assertion": "按影响范围只改 runtime;只 release 一次;未确认恢复前不可恢复" + }, + { + "family_id": "TDD-WP2-ERR-001", + "assertion": "三模型统一固定 category;提交前无任务,提交后只 release 一次;状态和用户动作符合第 9 节" + }, + { + "family_id": "TDD-WP2-GEN-002", + "assertion": "成功一图并 commit;失败/rejected 无输出并 release;重试同项目新任务" + } + ], + "task_green_gate": "reference_invalid 与 safety_rejected 分离;供应商原文不泄露;runtime 变化不回写 ModelConfig;用户点数只释放一次", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, ai-adapter-contract, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "UNIT", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:unit", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-BAL-001-balance-impact/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-gateway-balance/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-gateway-contract/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-model-disabled/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-reference-invalid/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-safety-rejected/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-unknown-non-retryable/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-unknown-retryable/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-upstream-failed/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-ERR-001-upstream-timeout/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-GEN-002-success/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "current-task", + "role": "implementation", + "frames": { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + }, + "applicable_states": { + "page_id": "current-task", + "actor_entry": "active 普通/内部预览账号", + "normal": "真实状态、结果入口", + "empty_loading": "无活动任务;queued/running 无假百分比", + "failure_disabled": "failed/rejected 与九类固定动作;第二任务禁用", + "conflict_capacity_special": "storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/current-task/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP2-07", + "work_package": "WP-2", + "title": "原图与 latest export 服务", + "dependencies": { + "source_text": "TASK-WP2-01、TASK-WP2-02、WP-0 存储", + "task_ids": [ + "TASK-WP2-01", + "TASK-WP2-02" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "EXPORT-04", + "EXPORT-05", + "EXPORT-06" + ], + "acceptance_criteria": [ + "AC-23", + "AC-35", + "AC-38", + "AC-55" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "EXPORT-04、AC-23、35", + "EXPORT-04、AC-23、38 P0-A 子集", + "EXPORT-05、06、AC-35、55" + ], + "acceptance_scope_notes": [ + "EXPORT-04、AC-23、38 P0-A 子集" + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "6.2 API 分组", + "9.5 客户端导出与 latest exports" + ], + "modules": [ + "api", + "sqlite", + "local-data-root", + "web" + ], + "implementation_boundary": "实现历史原图下载、latest JPG/PNG 资源关系、同 Blob 上传、后端失败矩阵和无导出成品空态", + "parent_families": [ + "TDD-WP2-EXP-001", + "TDD-WP2-EXP-002" + ], + "case_ids": [ + "TDD-WP2-EXP-001-result-matrix", + "TDD-WP2-EXP-001-same-blob-success", + "TDD-WP2-EXP-002-empty-latest" + ], + "first_red": [ + { + "family_id": "TDD-WP2-EXP-001", + "assertion": "项目/成品保存失败撤回下载或覆盖旧 latest" + }, + { + "family_id": "TDD-WP2-EXP-002", + "assertion": "从未导出项目自动生成默认成品" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP2-EXP-001", + "assertion": "同一次 Blob 用于下载和上传;下载可成功、latest 保持旧值;刷新只恢复 saved" + }, + { + "family_id": "TDD-WP2-EXP-002", + "assertion": "显示“暂无导出成品”,只提供进入编辑导出和下载原图" + } + ], + "task_green_gate": "客户端下载与后端保存结果独立;失败保留旧 latest;从未导出不自动合成;不承诺跨设备下载", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, local-data-root, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-EXP-001-result-matrix/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-EXP-001-same-blob-success/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP2-EXP-002-empty-latest/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-2", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "project-detail", + "role": "implementation", + "frames": { + "frame_id": "v2Qbu", + "frame_name": "03B 项目详情", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "历史、固定比例、继续生成、编辑、原图/latest;无成功图/无成品;10 图上限、资源失败和容量状态" + }, + "applicable_states": { + "page_id": "project-detail", + "actor_entry": "项目所有者", + "normal": "历史、固定比例、继续生成、编辑、原图/latest 下载", + "empty_loading": "无成功图草稿;历史/成品加载", + "failure_disabled": "10 图上限、当前底图不可删、无 latest、资源读取失败", + "conflict_capacity_special": "比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/project-detail/\u003cstate\u003e/" + }, + { + "page_id": "export", + "role": "implementation", + "frames": { + "frame_id": "i9upg7", + "frame_name": "04B 导出弹层", + "primary_task": "TASK-WP4-05", + "mapped_state_scope": "JPG/PNG、质量、尺寸;合成/持久化加载;未提交确认;四类失败结果;冲突一次下载和 full/unavailable" + }, + "applicable_states": { + "page_id": "export", + "actor_entry": "编辑器内项目所有者", + "normal": "JPG/PNG、质量、尺寸、下载/latest 分结果", + "empty_loading": "合成/持久化加载;无业务空态", + "failure_disabled": "合成、下载、项目保存、latest 保存分别失败;无画布/冲突次数用尽禁用", + "conflict_capacity_special": "未提交确认;conflicted 仅一次本地导出;full/unavailable 后端失败不撤回下载" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/export/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP3-01", + "work_package": "WP-3", + "title": "ModelConfig 集合与原子替换", + "dependencies": { + "source_text": "WP-2", + "task_ids": [ + + ], + "work_packages": [ + "WP-2" + ] + }, + "requirements": [ + "ADMIN-03", + "GEN-01", + "GEN-13", + "GEN-15" + ], + "acceptance_criteria": [ + "AC-30", + "AC-40" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.2" + ], + "requirement_ac_sources": [ + "ADMIN-03、17.2、AC-30", + "ADMIN-03、AC-30", + "GEN-01、ADMIN-03、17.2、AC-30", + "GEN-13、15、ADMIN-03、AC-30、40" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-MODEL-SEED", + "FX-MODEL-VERIFIED" + ], + "development_plan_sections": [ + "7.6 ModelConfig 原子配置与运行时推荐" + ], + "modules": [ + "api", + "sqlite", + "web-admin", + "shared-contracts" + ], + "implementation_boundary": "实现三模型固定 seed、immutable config_version/config_set、恰好一个 enabled default、唯一正整数 priority、完整集合 CAS、替代默认错误和审计", + "parent_families": [ + "TDD-WP3-MDL-001", + "TDD-WP3-MDL-002" + ], + "case_ids": [ + "TDD-WP3-MDL-001-seed-read", + "TDD-WP3-MDL-002-cas-conflict", + "TDD-WP3-MDL-002-contract-change", + "TDD-WP3-MDL-002-invalid-set", + "TDD-WP3-MDL-002-replace-default" + ], + "first_red": [ + { + "family_id": "TDD-WP3-MDL-001", + "assertion": "初始集合零/多默认、优先级非法或 runtime 假装 verified" + }, + { + "family_id": "TDD-WP3-MDL-002", + "assertion": "停用默认、重复优先级或 CAS 冲突产生部分配置" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP3-MDL-001", + "assertion": "三模型固定 seed;一个 enabled default;priority 1/2/3;contract unverified 时 runtime 不可用" + }, + { + "family_id": "TDD-WP3-MDL-002", + "assertion": "完整三模型集合原子校验和切换;失败原集合、runtime 和审计结果符合协议" + } + ], + "task_green_gate": "初始 1/2/3 优先级;缺失/非正/重复/零或多默认整批拒绝;切换无可见中间态;失败原集合不变", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web-admin, shared-contracts.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "UNIT", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:unit", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-001-seed-read/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-002-cas-conflict/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-002-contract-change/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-002-invalid-set/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-002-replace-default/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-3", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-models", + "role": "implementation", + "frames": { + "frame_id": "YVfVG", + "frame_name": "06B 模型配置", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "完整三模型集合、default/runtime/recommended;加载;空推荐;替代/priority 禁用;CAS 冲突和整套回滚" + }, + "applicable_states": { + "page_id": "admin-models", + "actor_entry": "active super_admin", + "normal": "完整三模型集合、default/runtime/recommended 三层", + "empty_loading": "初始加载骨架;无 recommended 是合法状态", + "failure_disabled": "合法替代/priority 前置禁用;契约未验证/运行时不可用", + "conflict_capacity_special": "config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-models/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP3-02", + "work_package": "WP-3", + "title": "独立 runtime 与推荐模型", + "dependencies": { + "source_text": "TASK-WP3-01、WP-2 余额状态", + "task_ids": [ + "TASK-WP3-01" + ], + "work_packages": [ + "WP-2" + ] + }, + "requirements": [ + "ADMIN-03", + "ADMIN-09", + "GEN-14", + "GEN-15" + ], + "acceptance_criteria": [ + "AC-30", + "AC-51" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.2" + ], + "requirement_ac_sources": [ + "GEN-14、15、ADMIN-09、AC-51", + "GEN-15、ADMIN-03、17.2、AC-30、51" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "7.6 ModelConfig 原子配置与运行时推荐", + "11.3 AI 网关余额" + ], + "modules": [ + "api", + "sqlite", + "web", + "web-admin" + ], + "implementation_boundary": "实现 runtime reason/version、configured default/runtime/recommended 三层读取、空推荐、人工恢复和余额影响审计", + "parent_families": [ + "TDD-WP3-BAL-001", + "TDD-WP3-MDL-003" + ], + "case_ids": [ + "TDD-WP3-BAL-001-confirmed-recovery", + "TDD-WP3-MDL-003-runtime-recommendation" + ], + "first_red": [ + { + "family_id": "TDD-WP3-BAL-001", + "assertion": "余额恢复自动执行或成本核对补扣用户点数" + }, + { + "family_id": "TDD-WP3-MDL-003", + "assertion": "runtime 降级改变 enabled/default/priority 或推荐为空时写回配置" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP3-BAL-001", + "assertion": "model/account/unknown 影响正确;人工确认恢复并审计;成本核对不改任务终态或用户点数" + }, + { + "family_id": "TDD-WP3-MDL-003", + "assertion": "配置字段不变;推荐只从可用候选按 priority 派生,可为 null" + } + ], + "task_green_gate": "降级/恢复不改 enabled/default/priority;推荐只从可用候选派生;成本核对不补扣用户点数", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web, web-admin.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "WRK", + "E2E", + "UNIT", + "API" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm test:unit", + "pnpm test:api", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-BAL-001-confirmed-recovery/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-MDL-003-runtime-recommendation/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-3", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-models", + "role": "implementation", + "frames": { + "frame_id": "YVfVG", + "frame_name": "06B 模型配置", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "完整三模型集合、default/runtime/recommended;加载;空推荐;替代/priority 禁用;CAS 冲突和整套回滚" + }, + "applicable_states": { + "page_id": "admin-models", + "actor_entry": "active super_admin", + "normal": "完整三模型集合、default/runtime/recommended 三层", + "empty_loading": "初始加载骨架;无 recommended 是合法状态", + "failure_disabled": "合法替代/priority 前置禁用;契约未验证/运行时不可用", + "conflict_capacity_special": "config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-models/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP3-03", + "work_package": "WP-3", + "title": "三个独立 Adapter 与契约证据", + "dependencies": { + "source_text": "TASK-WP3-01、WP-2 Generation 接口", + "task_ids": [ + "TASK-WP3-01" + ], + "work_packages": [ + "WP-2" + ] + }, + "requirements": [ + "ADMIN-03", + "GEN-01", + "GEN-02", + "GEN-05", + "GEN-06", + "GEN-13", + "GEN-15", + "GEN-16" + ], + "acceptance_criteria": [ + "AC-30", + "AC-40" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "GEN-01、02、05、06、13、16、AC-40", + "GEN-13、15、ADMIN-03、AC-30、40" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "8.2 固定 Adapter 接口", + "8.3 契约验证证据", + "8.5 九类错误统一路径" + ], + "modules": [ + "worker", + "ai-adapter-gemini-flash", + "ai-adapter-gemini-pro", + "ai-adapter-gpt-image", + "shared-contracts" + ], + "implementation_boundary": "分别实现三个 Adapter、纯文字/参考图/四比例、同步/异步、响应、一图、错误映射;配置字段变化令对应模型 unverified;提供 mock 和受控真实验证", + "parent_families": [ + "TDD-WP3-ADP-001", + "TDD-WP3-ADP-002" + ], + "case_ids": [ + "TDD-WP3-ADP-001-per-model-contract", + "TDD-WP3-ADP-002-invalidate-revalidate" + ], + "first_red": [ + { + "family_id": "TDD-WP3-ADP-001", + "assertion": "一个模型 fixture 被复用于另两个模型或静默接受多图" + }, + { + "family_id": "TDD-WP3-ADP-002", + "assertion": "契约字段变化后仍为 verified" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP3-ADP-001", + "assertion": "三个独立 Adapter 分别验证纯文字、参考图、四比例、同步/异步、响应和一图约束" + }, + { + "family_id": "TDD-WP3-ADP-002", + "assertion": "端点、字段、参考图、响应、异步查询或错误协议任一变化立即 unverified,完整重验后才恢复" + } + ], + "task_green_gate": "三模型 fixture/证据不得共用;完整重验前不可恢复 verified;真实证据未齐时任务保持 externally_blocked", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: worker, ai-adapter-gemini-flash, ai-adapter-gemini-pro, ai-adapter-gpt-image, shared-contracts.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "SQLite constraints, transactions, idempotency and side-effect assertions" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "EXT-MOCK", + "EXT-REAL", + "DB" + ], + "validation_commands": [ + "pnpm test:all", + "pnpm test:integration", + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gemini-3.1-flash-image-preview", + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gemini-3-pro-image-preview", + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gpt-image-2", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-ADP-001-per-model-contract/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-ADP-002-invalidate-revalidate/" + ], + "automation": [ + "automated", + "controlled_real" + ], + "release_gate": [ + "work_package:WP-3", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP3-04", + "work_package": "WP-3", + "title": "Worker 租约、崩溃恢复与结算接线", + "dependencies": { + "source_text": "TASK-WP3-03、TASK-WP2-06", + "task_ids": [ + "TASK-WP2-06", + "TASK-WP3-03" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "CREDIT-03", + "GEN-08", + "GEN-09" + ], + "acceptance_criteria": [ + "AC-04", + "AC-51" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "GEN-08、09、CREDIT-03、AC-04、51" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-GATEWAY-ASYNC", + "FX-GATEWAY-UNKNOWN" + ], + "development_plan_sections": [ + "8.1 持久队列", + "8.4 崩溃恢复" + ], + "modules": [ + "worker", + "sqlite", + "api" + ], + "implementation_boundary": "实现领取、租约、心跳、poll、崩溃恢复、无安全引用的 unknown_retryable、一次 release 和人工成本核对", + "parent_families": [ + "TDD-WP3-WRK-001" + ], + "case_ids": [ + "TDD-WP3-WRK-001-recovery" + ], + "first_red": [ + { + "family_id": "TDD-WP3-WRK-001", + "assertion": "Worker 崩溃后重新 submit、重复结算或丢任务" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP3-WRK-001", + "assertion": "租约/心跳;有引用只 poll;无安全引用则 unknown_retryable+release+pending_manual_review" + } + ], + "task_green_gate": "有上游引用只 poll、不重新 submit;未知结果终止用户任务且不双结算;Worker degraded 禁止新任务", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: worker, sqlite, api.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "SQLite constraints, transactions, idempotency and side-effect assertions" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "WRK" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:worker", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP3-WRK-001-recovery/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-3", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "current-task", + "role": "implementation", + "frames": { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + }, + "applicable_states": { + "page_id": "current-task", + "actor_entry": "active 普通/内部预览账号", + "normal": "真实状态、结果入口", + "empty_loading": "无活动任务;queued/running 无假百分比", + "failure_disabled": "failed/rejected 与九类固定动作;第二任务禁用", + "conflict_capacity_special": "storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/current-task/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-01", + "work_package": "WP-4", + "title": "编辑器壳、Canvas Schema 与底图", + "dependencies": { + "source_text": "WP-2;使用 WP-5 稳定 fixture", + "task_ids": [ + + ], + "work_packages": [ + "WP-2", + "WP-5" + ] + }, + "requirements": [ + "EDITOR-01", + "EDITOR-02", + "EDITOR-03", + "EXPORT-03", + "GEN-12", + "PROJECT-04" + ], + "acceptance_criteria": [ + "AC-07", + "AC-20", + "AC-23" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "EDITOR-01 至 03、PROJECT-04、EXPORT-03、AC-07、20、23", + "GEN-12、EDITOR-01、PROJECT-04、AC-07" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.3 画布状态与渲染", + "13.1 P0-A 性能预算", + "13.2 视觉一致性" + ], + "modules": [ + "web-editor", + "canvas-schema", + "api" + ], + "implementation_boundary": "按 UIDesign/低保真实现三栏编辑器、顶部/底部状态、Fabric 转换边界、底图填充/适配/裁剪/调色/滤镜/恢复原图和换底图", + "parent_families": [ + "TDD-WP4-BG-001", + "TDD-WP4-BG-002" + ], + "case_ids": [ + "TDD-WP4-BG-001-switch-background", + "TDD-WP4-BG-002-processing-controls" + ], + "first_red": [ + { + "family_id": "TDD-WP4-BG-001", + "assertion": "换底图丢覆盖层、保留旧调色或色卡不刷新" + }, + { + "family_id": "TDD-WP4-BG-002", + "assertion": "填充/适配/裁剪/缩放/定位或亮度、对比度、饱和度、色温、锐度、预设滤镜、恢复原图未保存、不可撤销或未进入导出" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-BG-001", + "assertion": "覆盖层位置/尺寸保留;底图参数重置;色卡重提;一次撤销和保存" + }, + { + "family_id": "TDD-WP4-BG-002", + "assertion": "每项参数可预览、提交、撤销、自动保存并在重开与导出中一致;恢复原图只重置底图处理,不改变覆盖元素" + } + ], + "task_green_gate": "底图参数可预览、提交、撤销、保存、重开和导出一致;换图保留覆盖层、重置底图处理并刷新色卡", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, canvas-schema, api.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-BG-001-switch-background/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-BG-002-processing-controls/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-02", + "work_package": "WP-4", + "title": "画布选择、变换与普通贴纸", + "dependencies": { + "source_text": "TASK-WP4-01", + "task_ids": [ + "TASK-WP4-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "EDITOR-04", + "EDITOR-05", + "EDITOR-06", + "EDITOR-07", + "EDITOR-08", + "EDITOR-09", + "PROJECT-04" + ], + "acceptance_criteria": [ + "AC-13", + "AC-27" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.5" + ], + "requirement_ac_sources": [ + "EDITOR-04 至 09、17.5、AC-27", + "EDITOR-04 至 09、PROJECT-04、AC-13、27" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CANVAS-50" + ], + "development_plan_sections": [ + "9.3 画布状态与渲染" + ], + "modules": [ + "web-editor", + "canvas-schema" + ], + "implementation_boundary": "实现循环选择、长按候选、多选/框选、移动/缩放/旋转/翻转/透明度/复制/层级/吸附、撤销重做和 50 元素双端限制", + "parent_families": [ + "TDD-WP4-CAN-001", + "TDD-WP4-STK-001" + ], + "case_ids": [ + "TDD-WP4-CAN-001-fifty-elements", + "TDD-WP4-STK-001-transform-sticker" + ], + "first_red": [ + { + "family_id": "TDD-WP4-CAN-001", + "assertion": "选择循环、多选、层级、吸附或第 51 元素错误" + }, + { + "family_id": "TDD-WP4-STK-001", + "assertion": "普通贴纸变换不进入保存/撤销/导出" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-CAN-001", + "assertion": "50 元素可编辑保存导出;第 51 个阻断;无图层面板/隐藏/锁定/编组" + }, + { + "family_id": "TDD-WP4-STK-001", + "assertion": "移动、缩放、旋转、翻转、透明度、复制、层级和多选一致" + } + ], + "task_green_gate": "50 元素可编辑保存导出,第 51 个被阻断;无图层面板、隐藏、锁定、编组;动态 UI 不改变工具尺寸", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, canvas-schema.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-CAN-001-fifty-elements/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-STK-001-transform-sticker/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-03", + "work_package": "WP-4", + "title": "文字模板、字体与属性", + "dependencies": { + "source_text": "TASK-WP4-01;WP-5 模板/字体 fixture", + "task_ids": [ + "TASK-WP4-01" + ], + "work_packages": [ + "WP-5" + ] + }, + "requirements": [ + "NFR-02", + "PROJECT-04", + "TEXT-01", + "TEXT-02", + "TEXT-03", + "TEXT-04", + "TEXT-05", + "TEXT-06", + "TEXT-07", + "TEXT-08", + "TEXT-09", + "TEXT-10", + "TEXT-11", + "TEXT-12", + "TEXT-13", + "TEXT-14" + ], + "acceptance_criteria": [ + "AC-09", + "AC-10", + "AC-11", + "AC-12", + "AC-32" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.6" + ], + "requirement_ac_sources": [ + "TEXT-01 至 06、PROJECT-04、AC-09、10", + "TEXT-01、17.6、AC-09、32", + "TEXT-07 至 14、NFR-02、AC-11、12" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.3 画布状态与渲染", + "9.4 素材面板与公开缓存" + ], + "modules": [ + "web-editor", + "asset-renderer", + "font-loader" + ], + "implementation_boundary": "实现必须先选模板、四类/显示名搜索/catalog 顺序/账号最近使用、多行内容、模板切换、FontFace ready、字号/缩放、填充/描边/背景/对齐/行距/字距和一次提交撤销", + "parent_families": [ + "TDD-WP4-TXT-001", + "TDD-WP4-TXT-002", + "TDD-WP4-TXT-003" + ], + "case_ids": [ + "TDD-WP4-TXT-001-multiline-template-switch", + "TDD-WP4-TXT-002-font-metrics-ranges", + "TDD-WP4-TXT-003-catalog-search-recent" + ], + "first_red": [ + { + "family_id": "TDD-WP4-TXT-001", + "assertion": "多行丢换行、空内容提交或切模板改变变换" + }, + { + "family_id": "TDD-WP4-TXT-002", + "assertion": "字体静默回退、字号与控制柄不同步、文字裁切、范围步长错误或背景透明度影响文字" + }, + { + "family_id": "TDD-WP4-TXT-003", + "assertion": "未选模板即可添加普通文字,或模板分类、显示名称搜索、catalog 顺序、账号最近使用漂移" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-TXT-001", + "assertion": "内容/换行持久;模板切换重置默认样式但保留位置/旋转/整体尺寸,可撤销" + }, + { + "family_id": "TDD-WP4-TXT-002", + "assertion": "真实归档字体;字号与缩放同步且内容重新测量不裁切;行距/字距/描边等精确范围;完成为一次撤销;重新打开和导出一致" + }, + { + "family_id": "TDD-WP4-TXT-003", + "assertion": "只能从模板添加;四类、显示名称搜索、稳定顺序和账号级最近使用符合 PRD;普通贴纸不复用该搜索" + } + ], + "task_green_gate": "文本/换行/变换按规则保留;真实字体缺失使模板 unavailable;不提供普通文字或普通贴纸关键词搜索;不静默回退", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, asset-renderer, font-loader.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-TXT-001-multiline-template-switch/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-TXT-002-font-metrics-ranges/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-TXT-003-catalog-search-recent/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-04", + "work_package": "WP-4", + "title": "色卡与动态贴纸 provider", + "dependencies": { + "source_text": "TASK-WP4-01;WP-5 声明式资源", + "task_ids": [ + "TASK-WP4-01" + ], + "work_packages": [ + "WP-5" + ] + }, + "requirements": [ + "COL-01", + "COL-02", + "COL-03", + "COL-04", + "COL-05", + "DYN-01", + "DYN-02", + "DYN-03", + "DYN-04", + "DYN-05", + "DYN-06", + "DYN-07", + "DYN-08", + "DYN-09", + "PRIV-03" + ], + "acceptance_criteria": [ + "AC-14", + "AC-15", + "AC-16", + "AC-17", + "AC-18", + "AC-19", + "AC-47" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "COL-01 至 05、AC-15 P0-A 子集、16", + "DYN-01 至 03、AC-14", + "DYN-04、PRIV-03、AC-17、47", + "DYN-05 至 09、AC-18、19" + ], + "acceptance_scope_notes": [ + "COL-01 至 05、AC-15 P0-A 子集、16" + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.3 画布状态与渲染", + "11.2 高德" + ], + "modules": [ + "web-editor", + "palette-provider", + "dynamic-provider", + "amap-adapter" + ], + "implementation_boundary": "实现 mmcq-v1 确定五色、四个色卡 renderer、时间/地点/身份 provider、DYN004 先确认后定位、坐标保存/清理、DYN012 FONT081 标记", + "parent_families": [ + "TDD-WP4-COL-001", + "TDD-WP4-DYN-001", + "TDD-WP4-DYN-002", + "TDD-WP4-DYN-003" + ], + "case_ids": [ + "TDD-WP4-COL-001-deterministic-palette", + "TDD-WP4-DYN-001-fixed-time", + "TDD-WP4-DYN-002-location-consent", + "TDD-WP4-DYN-003-identity-font" + ], + "first_red": [ + { + "family_id": "TDD-WP4-COL-001", + "assertion": "色卡随机、读取处理后底图或额外上传图片" + }, + { + "family_id": "TDD-WP4-DYN-001", + "assertion": "时间贴纸重开后变成新时间" + }, + { + "family_id": "TDD-WP4-DYN-002", + "assertion": "DYN004 提示前调用定位、拒绝后插入或删除后留坐标" + }, + { + "family_id": "TDD-WP4-DYN-003", + "assertion": "身份贴纸重复 @@、覆盖修改账号或 DYN012 静默换字体" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-COL-001", + "assertion": "同图 mmcq-v1 五色确定;裁剪/调色/滤镜不刷新;换底图刷新;网络上传为 0" + }, + { + "family_id": "TDD-WP4-DYN-001", + "assertion": "使用插入/确认值并持久化,导出静态不可点击内容" + }, + { + "family_id": "TDD-WP4-DYN-002", + "assertion": "同意后才定位并保存坐标;拒绝调用为 0;手动地点回退;删除/注销清理" + }, + { + "family_id": "TDD-WP4-DYN-003", + "assertion": "前缀一个;实例覆盖独立;DYN012 固定 FONT081 且证据标记替代" + } + ], + "task_green_gate": "色卡只读原始底图且网络上传为 0;时间值重开不漂移;拒绝定位调用为 0;身份前缀一个 @;DYN012 不静默换字体", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, palette-provider, dynamic-provider, amap-adapter.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-COL-001-deterministic-palette/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-DYN-001-fixed-time/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-DYN-002-location-consent/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-DYN-003-identity-font/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-05", + "work_package": "WP-4", + "title": "客户端导出与结果弹层", + "dependencies": { + "source_text": "TASK-WP4-01 至 TASK-WP4-04、TASK-WP2-07", + "task_ids": [ + "TASK-WP2-07", + "TASK-WP4-01", + "TASK-WP4-04" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "EXPORT-01", + "EXPORT-02", + "EXPORT-03", + "PROJECT-04", + "PROJECT-06" + ], + "acceptance_criteria": [ + "AC-23" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "EXPORT-01 至 03、AC-23", + "EXPORT-03、AC-23", + "PROJECT-04、06、EXPORT-03、AC-23" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CANVAS-RATIOS" + ], + "development_plan_sections": [ + "9.5 客户端导出与 latest exports" + ], + "modules": [ + "web-editor", + "export-compositor", + "api", + "local-data-root" + ], + "implementation_boundary": "实现四比例真实像素、sRGB、JPG 80-100/默认 92、PNG 无损、无水印、未提交修改确认、同一 Blob 下载/上传和四结果组合 UI", + "parent_families": [ + "TDD-WP4-EXP-001" + ], + "case_ids": [ + "TDD-WP4-EXP-001-cancel-pending-edit", + "TDD-WP4-EXP-001-confirm-pending-edit", + "TDD-WP4-EXP-001-format-ratio" + ], + "first_red": [ + { + "family_id": "TDD-WP4-EXP-001", + "assertion": "四比例像素、JPG/PNG、未提交修改或同 Blob 规则错误" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-EXP-001", + "assertion": "精确像素、sRGB、无水印、PNG 无损、JPG 80-100 默认 92;未提交文字或样式经确认后,提交形成且仅形成一次撤销记录并进入正常自动保存,再用提交后的同一画布状态合成;真正的导出动作不额外创建撤销记录" + } + ], + "task_green_gate": "确认未提交修改只形成一次撤销并进入自动保存;导出本身不额外建撤销;后端失败不撤回已成功下载", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, export-compositor, api, local-data-root.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-EXP-001-cancel-pending-edit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-EXP-001-confirm-pending-edit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-EXP-001-format-ratio/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + }, + { + "page_id": "export", + "role": "implementation", + "frames": { + "frame_id": "i9upg7", + "frame_name": "04B 导出弹层", + "primary_task": "TASK-WP4-05", + "mapped_state_scope": "JPG/PNG、质量、尺寸;合成/持久化加载;未提交确认;四类失败结果;冲突一次下载和 full/unavailable" + }, + "applicable_states": { + "page_id": "export", + "actor_entry": "编辑器内项目所有者", + "normal": "JPG/PNG、质量、尺寸、下载/latest 分结果", + "empty_loading": "合成/持久化加载;无业务空态", + "failure_disabled": "合成、下载、项目保存、latest 保存分别失败;无画布/冲突次数用尽禁用", + "conflict_capacity_special": "未提交确认;conflicted 仅一次本地导出;full/unavailable 后端失败不撤回下载" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/export/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-06", + "work_package": "WP-4", + "title": "键盘、焦点与语义可访问性", + "dependencies": { + "source_text": "TASK-WP4-01 至 TASK-WP4-05", + "task_ids": [ + "TASK-WP4-01", + "TASK-WP4-05" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-06" + ], + "acceptance_criteria": [ + "AC-32" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-06、AC-32" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "13.3 可访问性基础" + ], + "modules": [ + "web", + "web-editor" + ], + "implementation_boundary": "落实 skip link、焦点顺序、ARIA/live region、弹层焦点/返回、画布外等价控件、200% 缩放和 axe", + "parent_families": [ + "TDD-WP4-A11Y-001" + ], + "case_ids": [ + "TDD-WP4-A11Y-001-keyboard-focus" + ], + "first_red": [ + { + "family_id": "TDD-WP4-A11Y-001", + "assertion": "关键动作不可键盘到达、焦点逃逸或状态只靠颜色" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-A11Y-001", + "assertion": "焦点顺序、ARIA、live region、弹层返回、等价画布工具、axe critical/serious 为 0" + } + ], + "task_green_gate": "axe critical/serious 为 0;状态不只靠颜色;异步更新不抢焦点;最长文案、弹层和主动作可达", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web, web-editor.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "E2E", + "VIS-PERF" + ], + "validation_commands": [ + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-A11Y-001-keyboard-focus/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP4-07", + "work_package": "WP-4", + "title": "视觉与性能预算", + "dependencies": { + "source_text": "TASK-WP4-01 至 TASK-WP4-06;最终素材依赖 WP-5", + "task_ids": [ + "TASK-WP4-01", + "TASK-WP4-06" + ], + "work_packages": [ + "WP-5" + ] + }, + "requirements": [ + "NFR-02", + "NFR-03" + ], + "acceptance_criteria": [ + "AC-19", + "AC-23", + "AC-27", + "AC-32" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-02、AC-19、23、32", + "NFR-03、AC-27、32" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-CANVAS-50" + ], + "development_plan_sections": [ + "13.1 P0-A 性能预算", + "13.2 视觉一致性" + ], + "modules": [ + "web-editor", + "test-visual-performance" + ], + "implementation_boundary": "建立固定 Canvas/字体/动态值、Chrome/Edge 截图 diff、50 元素交互、自动保存、编辑器打开、导出、内存和 DOM 性能 harness", + "parent_families": [ + "TDD-WP4-PERF-001", + "TDD-WP4-VIS-001" + ], + "case_ids": [ + "TDD-WP4-PERF-001-budget", + "TDD-WP4-VIS-001-browser-diff" + ], + "first_red": [ + { + "family_id": "TDD-WP4-PERF-001", + "assertion": "50 元素、自动保存、资源面板或导出超过预算" + }, + { + "family_id": "TDD-WP4-VIS-001", + "assertion": "Chrome/Edge 白名单结构或导出漂移" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP4-PERF-001", + "assertion": "第 10.2 节全部预算通过,失败不破坏项目或 latest export" + }, + { + "family_id": "TDD-WP4-VIS-001", + "assertion": "固定环境按第 10 节阈值比较;已知替代项人工复核" + } + ], + "task_green_gate": "边界偏差、显著像素和全部性能预算符合 tdd;已知替代人工复核;不得通过放宽超时、减元素或降尺寸掩盖失败", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-editor, test-visual-performance.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "VIS-PERF", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-PERF-001-budget/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP4-VIS-001-browser-diff/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-4", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "editor", + "role": "implementation", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP5-01", + "work_package": "WP-5", + "title": "只读素材编译器", + "dependencies": { + "source_text": "WP-0", + "task_ids": [ + + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "COL-01", + "COL-02", + "COL-03", + "COL-04", + "COL-05", + "DYN-01", + "DYN-02", + "DYN-03", + "DYN-04", + "DYN-05", + "DYN-06", + "DYN-07", + "DYN-08", + "DYN-09", + "STATIC-01", + "STATIC-02", + "STATIC-03", + "STATIC-04", + "TEXT-01", + "TEXT-02", + "TEXT-03", + "TEXT-04", + "TEXT-05", + "TEXT-06", + "TEXT-07", + "TEXT-08", + "TEXT-09", + "TEXT-10", + "TEXT-11", + "TEXT-12", + "TEXT-13", + "TEXT-14" + ], + "acceptance_criteria": [ + "AC-32", + "AC-42" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、STATIC-01 至 STATIC-04、AC-32、42" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "10.1 唯一输入", + "10.3 复杂资源包" + ], + "modules": [ + "asset-compiler", + "asset-metadata", + "local-data-root" + ], + "implementation_boundary": "建立 TypeScript CLI,只读读取规范 manifest/catalog/metadata,输出 TemplateRenderModel、版本化元数据、最小 derived 产物和源前后校验报告;不执行 Lua/Prefab", + "parent_families": [ + "TDD-WP5-MAN-001" + ], + "case_ids": [ + "TDD-WP5-MAN-001-readonly-compiler" + ], + "first_red": [ + { + "family_id": "TDD-WP5-MAN-001", + "assertion": "编译器读取证据目录、执行 Lua/Prefab、复制全库或改写源" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-MAN-001", + "assertion": "只读规范入口;声明式转换;源前后哈希/时间不变;无等价全库副本" + } + ], + "task_green_gate": "源文件哈希/时间不变;不读证据目录;不复制全库或形成等价派生副本;内容哈希去重复用", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: asset-compiler, asset-metadata, local-data-root.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "PKG-SEC", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-MAN-001-readonly-compiler/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP5-02", + "work_package": "WP-5", + "title": "普通贴纸 catalog 与虚拟目录", + "dependencies": { + "source_text": "TASK-WP5-01", + "task_ids": [ + "TASK-WP5-01" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "STATIC-01", + "STATIC-02", + "STATIC-03", + "STATIC-04" + ], + "acceptance_criteria": [ + "AC-13", + "AC-32" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "STATIC-01 至 04、AC-13、32" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.4 素材面板与公开缓存", + "10.2 StaticStickerCatalog 构建" + ], + "modules": [ + "asset-compiler", + "static-sticker-catalog", + "web" + ], + "implementation_boundary": "构建 part1-25 的 1,407 项 StaticStickerCatalog,保留三组重复、稳定 ID/顺序/哈希/尺寸/MIME/缩略图引用;接入虚拟列表和懒加载", + "parent_families": [ + "TDD-WP5-CAT-001" + ], + "case_ids": [ + "TDD-WP5-CAT-001-catalog-1407" + ], + "first_red": [ + { + "family_id": "TDD-WP5-CAT-001", + "assertion": "计数、part、重复项、顺序或懒加载错误" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-CAT-001", + "assertion": "part1-25 共 1,407 PNG;3 组重复保留;虚拟列表节点不线性增长,原图按需加载" + } + ], + "task_green_gate": "计数、part、重复和顺序精确;原图按需加载;DOM 只保留视口和两屏缓冲", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: asset-compiler, static-sticker-catalog, web.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-CAT-001-catalog-1407/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP5-03", + "work_package": "WP-5", + "title": "复杂资源注册与 P0-A 白名单", + "dependencies": { + "source_text": "TASK-WP5-01;与 WP-4 renderer 契约联调", + "task_ids": [ + "TASK-WP5-01" + ], + "work_packages": [ + "WP-4" + ] + }, + "requirements": [ + "ADMIN-07", + "COL-01", + "COL-02", + "COL-03", + "COL-04", + "COL-05", + "DYN-01", + "DYN-08", + "NFR-02", + "NFR-07", + "STATIC-01", + "TEXT-01", + "TEXT-04" + ], + "acceptance_criteria": [ + "AC-15", + "AC-16", + "AC-32", + "AC-42", + "AC-48" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "COL-01 至 05、AC-15 P0-A 子集、16、32", + "TEXT-01、TEXT-04、COL-01、DYN-01、DYN-08、STATIC-01、ADMIN-07、NFR-02、NFR-07、AC-32、42、48" + ], + "acceptance_scope_notes": [ + "COL-01 至 05、AC-15 P0-A 子集、16、32" + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "10.3 复杂资源包", + "10.5 Manifest 与发布路径" + ], + "modules": [ + "asset-compiler", + "template-registry", + "asset-renderer" + ], + "implementation_boundary": "导入注册 332 文字、86 字体项、16 色卡、35 动态;公开 manifest 只启用 32 文字、实际字体+FONT081、COLOR001/002/008/016、10 动态和 1,407 普通贴纸", + "parent_families": [ + "TDD-WP5-COL-001", + "TDD-WP5-WHITE-001" + ], + "case_ids": [ + "TDD-WP5-COL-001-four-layouts", + "TDD-WP5-WHITE-001-p0a-allowlist" + ], + "first_red": [ + { + "family_id": "TDD-WP5-COL-001", + "assertion": "P0-A 启用非四种色卡或四种布局不消费同一五色" + }, + { + "family_id": "TDD-WP5-WHITE-001", + "assertion": "普通用户发现白名单外复杂素材或缺少规定白名单" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-COL-001", + "assertion": "只启用 COLOR001/002/008/016,统一五色,刷新规则与 WP-4 一致" + }, + { + "family_id": "TDD-WP5-WHITE-001", + "assertion": "精确 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 普通贴纸" + } + ], + "task_green_gate": "白名单外复杂素材不对普通用户可见;四种色卡消费同一五色;色卡/动态不计入 25 part;不把 full_p0 分批公开", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: asset-compiler, template-registry, asset-renderer.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "E2E", + "VIS-PERF", + "API" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm test:api", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-COL-001-four-layouts/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-WHITE-001-p0a-allowlist/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP5-04", + "work_package": "WP-5", + "title": "Immutable manifest 与三类资源隔离", + "dependencies": { + "source_text": "TASK-WP5-01 至 TASK-WP5-03、WP-0 gate/cache", + "task_ids": [ + "TASK-WP5-01", + "TASK-WP5-03" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "NFR-07", + "PRIV-02", + "PRIV-05" + ], + "acceptance_criteria": [ + "AC-42", + "AC-46", + "AC-48" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.12", + "17.13", + "17.14" + ], + "requirement_ac_sources": [ + "NFR-07、17.14、AC-46、48", + "PRIV-02、05、17.12、13、AC-42、48" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.4 素材面板与公开缓存", + "10.5 Manifest 与发布路径", + "10.6 三类资源隔离" + ], + "modules": [ + "api", + "asset-release-manifest", + "web-client-cache" + ], + "implementation_boundary": "实现 release_version、manifest/file SHA-256、public/preview/private 三类路由、鉴权、no-store/immutable 缓存和不可推导资源 URL", + "parent_families": [ + "TDD-WP5-CACHE-001", + "TDD-WP5-RES-001" + ], + "case_ids": [ + "TDD-WP5-CACHE-001-no-private-client-state", + "TDD-WP5-RES-001-three-access-classes" + ], + "first_red": [ + { + "family_id": "TDD-WP5-CACHE-001", + "assertion": "SW 拦截 internal/private 或 IndexedDB 保存私有字段" + }, + { + "family_id": "TDD-WP5-RES-001", + "assertion": "公开 manifest 暴露 internal/private、路径可推导或缓存策略混用" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-CACHE-001", + "assertion": "仅公开 allowlist;每场景实际枚举 registration、Cache keys 和 IndexedDB 记录" + }, + { + "family_id": "TDD-WP5-RES-001", + "assertion": "三类路由、鉴权和 cache header 分离;浏览器永不获得绝对路径" + } + ], + "task_green_gate": "public manifest 不含 internal/private;浏览器不见绝对路径;SW/Cache/IndexedDB 实际枚举无私有或预览内容", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, asset-release-manifest, web-client-cache.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "E2E", + "API", + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:e2e", + "pnpm test:api", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-CACHE-001-no-private-client-state/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-RES-001-three-access-classes/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "preview-user-variant", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "有效授权、无授权/空批次、加载、撤销/过期阻断;不出现后台权限;全局容量条" + }, + "applicable_states": { + "page_id": "preview-user-variant", + "actor_entry": "有有效/撤销/过期 AssetPreviewGrant 的普通账号", + "normal": "授权批次素材显示“预览”,其余页面同普通用户", + "empty_loading": "无授权/批次空;manifest 加载", + "failure_disabled": "撤销/过期立即阻断新读取", + "conflict_capacity_special": "不出现后台入口、调点、邀请码、模型或私有内容;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/preview-user-variant/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP5-05", + "work_package": "WP-5", + "title": "后台普通贴纸上传与版本发布", + "dependencies": { + "source_text": "TASK-WP5-02、TASK-WP5-04、WP-0 存储", + "task_ids": [ + "TASK-WP5-02", + "TASK-WP5-04" + ], + "work_packages": [ + "WP-0" + ] + }, + "requirements": [ + "ADMIN-06" + ], + "acceptance_criteria": [ + "AC-31", + "AC-55" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.7", + "17.19" + ], + "requirement_ac_sources": [ + "ADMIN-06、17.7、17.19、AC-31、55" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "10.4 后台新增普通贴纸" + ], + "modules": [ + "api", + "worker", + "sqlite", + "local-data-root", + "web-admin" + ], + "implementation_boundary": "实现 PNG/WebP 流式校验、stable ID/part/顺序、原图与缩略图独立预留/计量、immutable release 切换和旧版本保留", + "parent_families": [ + "TDD-WP5-UPL-001" + ], + "case_ids": [ + "TDD-WP5-UPL-001-upload-metering" + ], + "first_red": [ + { + "family_id": "TDD-WP5-UPL-001", + "assertion": "后台上传未流式校验、原图/缩略图漏计量或 full 仍写入" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-UPL-001", + "assertion": "PNG/WebP、稳定 ID、part、顺序和 MIME 校验;两类文件分别预留并计量" + } + ], + "task_green_gate": "两类文件均计入 5 GB;full/unavailable 阻断;恰好等于上限允许提交后 full;不把既有归档复制进 managed-assets", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, local-data-root, web-admin.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-UPL-001-upload-metering/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-assets", + "role": "implementation", + "frames": { + "frame_id": "A28V3c", + "frame_name": "06C 素材与历史清理", + "primary_task": "TASK-WP5-06", + "mapped_state_scope": "上传/版本/候选;无上传/无候选;格式/编译/权限失败;full/unavailable;引用冲突、排队、物理删除和重计量" + }, + "applicable_states": { + "page_id": "admin-assets", + "actor_entry": "active super_admin", + "normal": "普通贴纸、资源版本/发布、历史清理", + "empty_loading": "无上传素材/无候选;列表/处理加载", + "failure_disabled": "格式/编译失败;权限、文件状态禁用", + "conflict_capacity_special": "stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-assets/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP5-06", + "work_package": "WP-5", + "title": "历史贴纸显式清理", + "dependencies": { + "source_text": "TASK-WP5-05、WP-2 project_asset_refs、WP-1 审计", + "task_ids": [ + "TASK-WP5-05" + ], + "work_packages": [ + "WP-1", + "WP-2" + ] + }, + "requirements": [ + "ADMIN-06", + "ADMIN-09" + ], + "acceptance_criteria": [ + "AC-31", + "AC-50", + "AC-55" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.7", + "17.19" + ], + "requirement_ac_sources": [ + "ADMIN-06、09、17.7、17.19、AC-31、50、55" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "7.7 后台贴纸历史文件显式清理" + ], + "modules": [ + "api", + "worker", + "sqlite", + "local-data-root", + "web-admin" + ], + "implementation_boundary": "实现候选查询、同管理员确认、短期 snapshot、BEGIN IMMEDIATE 全引用复核、整批拒绝/排队、Worker 物理删除、重计量和全阶段审计", + "parent_families": [ + "TDD-WP5-CLN-001" + ], + "case_ids": [ + "TDD-WP5-CLN-001-reference-race" + ], + "first_red": [ + { + "family_id": "TDD-WP5-CLN-001", + "assertion": "自动清理、有引用仍删除、整批部分排队或排队即减容量" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-CLN-001", + "assertion": "候选、同管理员确认、BEGIN IMMEDIATE 复核、整批拒绝/排队、物理删除、重测和全阶段审计" + } + ], + "task_green_gate": "有任一 release/active/trashed/待发布引用则整批拒绝;排队不减容量;无自动清理;物理删除和重测后才恢复", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, worker, sqlite, local-data-root, web-admin.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-CLN-001-reference-race/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-assets", + "role": "implementation", + "frames": { + "frame_id": "A28V3c", + "frame_name": "06C 素材与历史清理", + "primary_task": "TASK-WP5-06", + "mapped_state_scope": "上传/版本/候选;无上传/无候选;格式/编译/权限失败;full/unavailable;引用冲突、排队、物理删除和重计量" + }, + "applicable_states": { + "page_id": "admin-assets", + "actor_entry": "active super_admin", + "normal": "普通贴纸、资源版本/发布、历史清理", + "empty_loading": "无上传素材/无候选;列表/处理加载", + "failure_disabled": "格式/编译失败;权限、文件状态禁用", + "conflict_capacity_special": "stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-assets/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP5-07", + "work_package": "WP-5", + "title": "内部预览授权生命周期", + "dependencies": { + "source_text": "TASK-WP5-03、TASK-WP5-04、WP-1 会话", + "task_ids": [ + "TASK-WP5-03", + "TASK-WP5-04" + ], + "work_packages": [ + "WP-1" + ] + }, + "requirements": [ + "ADMIN-07" + ], + "acceptance_criteria": [ + "AC-42", + "AC-48" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.13" + ], + "requirement_ac_sources": [ + "ADMIN-07、17.13、AC-42、48" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.7 内部预览授权" + ], + "modules": [ + "api", + "sqlite", + "web", + "web-admin" + ], + "implementation_boundary": "实现批次、AssetPreviewGrant、active/revoked/expired、每次授权校验、随机 manifest item ID、撤销即时阻断和既有项目按版本渲染", + "parent_families": [ + "TDD-WP5-PREV-001" + ], + "case_ids": [ + "TDD-WP5-PREV-001-grant-lifecycle" + ], + "first_red": [ + { + "family_id": "TDD-WP5-PREV-001", + "assertion": "授权改变角色、公开 manifest 泄漏或撤销后仍可取资源" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP5-PREV-001", + "assertion": "每次校验 grant;no-store;撤销立即阻断新访问;已有项目按版本渲染" + } + ], + "task_green_gate": "授权不改变普通用户角色;不出现后台能力;撤销/到期阻断新取用但不破坏已保存项目", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: api, sqlite, web, web-admin.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP5-PREV-001-grant-lifecycle/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-5", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "preview-user-variant", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "有效授权、无授权/空批次、加载、撤销/过期阻断;不出现后台权限;全局容量条" + }, + "applicable_states": { + "page_id": "preview-user-variant", + "actor_entry": "有有效/撤销/过期 AssetPreviewGrant 的普通账号", + "normal": "授权批次素材显示“预览”,其余页面同普通用户", + "empty_loading": "无授权/批次空;manifest 加载", + "failure_disabled": "撤销/过期立即阻断新读取", + "conflict_capacity_special": "不出现后台入口、调点、邀请码、模型或私有内容;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/preview-user-variant/\u003cstate\u003e/" + }, + { + "page_id": "admin-preview", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "批次、模板状态、授权/撤销/到期、空/骨架、非法转换/发布失败;不出现 P0-B 全量开放" + }, + "applicable_states": { + "page_id": "admin-preview", + "actor_entry": "active super_admin", + "normal": "批次、模板状态、账号授权/撤销/到期", + "empty_loading": "无批次/授权;表格骨架", + "failure_disabled": "非法状态转换、过期、发布校验失败", + "conflict_capacity_special": "授权不改变角色;P0-B 全量开放不可在 P0-A 出现;继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-preview/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP6-01", + "work_package": "WP-6", + "title": "后台壳、权限与安全摘要", + "dependencies": { + "source_text": "WP-1、WP-2、WP-5", + "task_ids": [ + + ], + "work_packages": [ + "WP-1", + "WP-2", + "WP-5" + ] + }, + "requirements": [ + "ADMIN-01", + "ADMIN-02", + "ADMIN-04", + "ADMIN-08" + ], + "acceptance_criteria": [ + "AC-25", + "AC-49" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "ADMIN-01、02、04、08、AC-25、49" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.6 运营后台与两类审计" + ], + "modules": [ + "web-admin", + "api", + "sqlite" + ], + "implementation_boundary": "按 UIDesign 实现独立 /admin 路由域、侧栏/总览、用户、邀请码、点数、素材、预览、生成记录入口和 active super_admin 服务端授权", + "parent_families": [ + "TDD-WP6-ADM-001" + ], + "case_ids": [ + "TDD-WP6-ADM-001-role-and-summary" + ], + "first_red": [ + { + "family_id": "TDD-WP6-ADM-001", + "assertion": "普通/预览账号进入后台、后台显示私有正文或敏感配置" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP6-ADM-001", + "assertion": "active super_admin 服务端授权;默认仅安全摘要;角色和会话域严格分离" + } + ], + "task_green_gate": "普通/预览账号无法进入;管理员与普通 session 域分离;默认列表只含脱敏摘要;无敏感配置回显", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-admin, api, sqlite.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-ADM-001-role-and-summary/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-6", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-overview", + "role": "implementation", + "frames": { + "frame_id": "e4e2Kz", + "frame_name": "06A 后台总览", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "名额、任务、成本核对、服务/容量和审计摘要;骨架/空事件;聚合失败;三模型状态分列和待处理事项" + }, + "applicable_states": { + "page_id": "admin-overview", + "actor_entry": "active super_admin", + "normal": "名额、任务、模型、点数、服务、容量、审计摘要", + "empty_loading": "无近期事件;指标骨架", + "failure_disabled": "聚合失败保留旧值和时间;敏感动作按状态禁用", + "conflict_capacity_special": "default/runtime/recommended 分列;pending_manual_review 为成本核对;容量持续条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-overview/\u003cstate\u003e/" + }, + { + "page_id": "admin-users", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "脱敏列表/详情、暂停恢复/调点、空/筛选空/骨架、原因缺失和状态禁用、负 available" + }, + "applicable_states": { + "page_id": "admin-users", + "actor_entry": "active super_admin", + "normal": "脱敏列表/详情、暂停恢复、调点", + "empty_loading": "无用户/筛选空;表格骨架", + "failure_disabled": "操作失败、原因缺失、状态前置禁用", + "conflict_capacity_special": "调点可使 available 为负但 reserved 不变;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-users/\u003cstate\u003e/" + }, + { + "page_id": "admin-invites", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "创建/停用/期限/次数/脱敏复制、空/骨架、名额满、过期/耗尽、请求失败和幂等/CAS 结果" + }, + "applicable_states": { + "page_id": "admin-invites", + "actor_entry": "active super_admin", + "normal": "创建、停用、期限/次数、脱敏复制", + "empty_loading": "无邀请码;表格骨架", + "failure_disabled": "名额满、停用/过期/耗尽不可当可用;请求失败", + "conflict_capacity_special": "CAS/幂等结果;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-invites/\u003cstate\u003e/" + }, + { + "page_id": "admin-assets", + "role": "implementation", + "frames": { + "frame_id": "A28V3c", + "frame_name": "06C 素材与历史清理", + "primary_task": "TASK-WP5-06", + "mapped_state_scope": "上传/版本/候选;无上传/无候选;格式/编译/权限失败;full/unavailable;引用冲突、排队、物理删除和重计量" + }, + "applicable_states": { + "page_id": "admin-assets", + "actor_entry": "active super_admin", + "normal": "普通贴纸、资源版本/发布、历史清理", + "empty_loading": "无上传素材/无候选;列表/处理加载", + "failure_disabled": "格式/编译失败;权限、文件状态禁用", + "conflict_capacity_special": "stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-assets/\u003cstate\u003e/" + }, + { + "page_id": "admin-preview", + "role": "implementation", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "批次、模板状态、授权/撤销/到期、空/骨架、非法转换/发布失败;不出现 P0-B 全量开放" + }, + "applicable_states": { + "page_id": "admin-preview", + "actor_entry": "active super_admin", + "normal": "批次、模板状态、账号授权/撤销/到期", + "empty_loading": "无批次/授权;表格骨架", + "failure_disabled": "非法状态转换、过期、发布校验失败", + "conflict_capacity_special": "授权不改变角色;P0-B 全量开放不可在 P0-A 出现;继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-preview/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP6-02", + "work_package": "WP-6", + "title": "私有内容告知与逐次访问", + "dependencies": { + "source_text": "TASK-WP6-01、WP-1 UserProfile/审计", + "task_ids": [ + "TASK-WP6-01" + ], + "work_packages": [ + "WP-1" + ] + }, + "requirements": [ + "ADMIN-05" + ], + "acceptance_criteria": [ + "AC-25", + "AC-50" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.1", + "17.10" + ], + "requirement_ac_sources": [ + "ADMIN-05、17.1、AC-25", + "ADMIN-05、17.10、AC-25", + "ADMIN-05、17.10、AC-25、50", + "ADMIN-05、AC-25" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + "FX-ACTOR-ADMIN-A" + ], + "development_plan_sections": [ + "7.8 私有内容告知确认与逐次访问", + "9.6 运营后台与两类审计" + ], + "modules": [ + "web-admin", + "api", + "sqlite", + "local-data-root" + ], + "implementation_boundary": "实现当前 notice version bootstrap、身份+版本持久 ack、428 区域门禁、升版重阻断、“打开并记录审计”和先审计后返回内容", + "parent_families": [ + "TDD-WP6-PRIV-001", + "TDD-WP6-PRIV-002" + ], + "case_ids": [ + "TDD-WP6-PRIV-001-ack", + "TDD-WP6-PRIV-001-persistence", + "TDD-WP6-PRIV-001-version-bump", + "TDD-WP6-PRIV-002-deny-without-audit", + "TDD-WP6-PRIV-002-open-after-audit" + ], + "first_red": [ + { + "family_id": "TDD-WP6-PRIV-001", + "assertion": "告知确认只存在 Cookie/session/localStorage 或同版本重复" + }, + { + "family_id": "TDD-WP6-PRIV-002", + "assertion": "hover/预取打开内容、确认替代逐次审计、要求填写访问目的/原因或审计失败仍返回内容" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP6-PRIV-001", + "assertion": "UserProfile 按 admin+version 持久;跨标签、浏览器/API 重启、重登和 Chrome/Edge不重复;升版重阻断" + }, + { + "family_id": "TDD-WP6-PRIV-002", + "assertion": "每个入口原样显示“打开并记录审计”;每次点击先提交独立 PrivateContentAccessLog;UI 和 API 均不要求逐次填写访问目的、原因或二次确认;审计只记录 PRD/DevelopmentPlan 允许的对象、管理员、时间、内容类型等非正文信息;失败不返回;列表/HTML/缓存无正文" + } + ], + "task_green_gate": "跨标签、重启、重登和 Chrome/Edge 同版本不重复;hover/预取不打开;每次点击独立记录;审计失败不返回内容;不要求逐次填写原因", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-admin, api, sqlite, local-data-root.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-PRIV-001-ack/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-PRIV-001-persistence/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-PRIV-001-version-bump/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-PRIV-002-deny-without-audit/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-PRIV-002-open-after-audit/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-6", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-generations", + "role": "implementation", + "frames": { + "frame_id": "OnsiT", + "frame_name": "06D 生成记录与私有告知", + "primary_task": "TASK-WP6-02", + "mapped_state_scope": "默认隐藏正文;身份+版本告知;逐次打开;空记录/审计写入加载;428、审计失败、升版重阻断" + }, + "applicable_states": { + "page_id": "admin-generations", + "actor_entry": "active super_admin", + "normal": "默认安全摘要、告知、逐次打开", + "empty_loading": "无记录;列表/审计写入加载", + "failure_disabled": "未确认 428、审计失败不打开、权限禁用", + "conflict_capacity_special": "告知升版重阻断;每次打开先独立审计;页面读取不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-generations/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP6-03", + "work_package": "WP-6", + "title": "免费服务硬停止与恢复", + "dependencies": { + "source_text": "TASK-WP6-01、WP-1/2 事务基础", + "task_ids": [ + "TASK-WP6-01" + ], + "work_packages": [ + "WP-1" + ] + }, + "requirements": [ + "ADMIN-05", + "ADMIN-08", + "ADMIN-09", + "NFR-08" + ], + "acceptance_criteria": [ + "AC-47", + "AC-52" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.15" + ], + "requirement_ac_sources": [ + "ADMIN-05、08、NFR-08、17.15、AC-47", + "ADMIN-08、09、NFR-08、AC-47、52" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "11.1 Resend", + "11.2 高德" + ], + "modules": [ + "web-admin", + "api", + "sqlite", + "resend-adapter", + "amap-adapter" + ], + "implementation_boundary": "实现 Resend 每日/月度、高德月度用量领取,paused_quota/provider、上限只可下调、健康检查和 super_admin 人工恢复", + "parent_families": [ + "TDD-WP6-SVC-001", + "TDD-WP6-SVC-002" + ], + "case_ids": [ + "TDD-WP6-SVC-001-hard-quota", + "TDD-WP6-SVC-002-limit-recovery" + ], + "first_red": [ + { + "family_id": "TDD-WP6-SVC-001", + "assertion": "Resend 发第 81 封/超月限、高德发第 1001 次或暂停后伪造成功" + }, + { + "family_id": "TDD-WP6-SVC-002", + "assertion": "后台提高上限、启用收费或自动恢复" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP6-SVC-001", + "assertion": "配额领取事务化;paused_quota/provider;已有会话继续;DYN004 停而手动地点继续" + }, + { + "family_id": "TDD-WP6-SVC-002", + "assertion": "只允许不扩大能力的下调并审计;提高/付费/自动扩容被拒绝;恢复需前置确认" + } + ], + "task_green_gate": "第 81/1001 次不调用;不得提高固定上限、启用收费、自动恢复/扩容;高德暂停只影响 DYN004 自动定位", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-admin, api, sqlite, resend-adapter, amap-adapter.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "EXT-MOCK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:all", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-SVC-001-hard-quota/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-SVC-002-limit-recovery/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-6", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-services-storage", + "role": "implementation", + "frames": { + "frame_id": "L3lde2", + "frame_name": "06E 服务与存储", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "Resend/高德/网关/Worker/LocalDataRoot;指标加载;paused/degraded/unavailable;恢复前置、清理排队和重计量" + }, + "applicable_states": { + "page_id": "admin-services-storage", + "actor_entry": "active super_admin", + "normal": "Resend/高德/网关/Worker/LocalDataRoot 状态与允许动作", + "empty_loading": "指标加载;无告警", + "failure_disabled": "paused/degraded/unavailable;不合法恢复/提高上限禁用", + "conflict_capacity_special": "full/unavailable、清理排队和物理重计量;不显示绝对路径" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-services-storage/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP6-04", + "work_package": "WP-6", + "title": "敏感操作审计与保留", + "dependencies": { + "source_text": "TASK-WP6-01 至 TASK-WP6-03、WP-5 清理", + "task_ids": [ + "TASK-WP6-01", + "TASK-WP6-03" + ], + "work_packages": [ + "WP-5" + ] + }, + "requirements": [ + "ADMIN-09" + ], + "acceptance_criteria": [ + "AC-50" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + "17.16" + ], + "requirement_ac_sources": [ + "ADMIN-09、17.16、AC-50" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "9.6 运营后台与两类审计", + "12.3 审计与敏感信息" + ], + "modules": [ + "web-admin", + "api", + "worker", + "sqlite" + ], + "implementation_boundary": "把用户、点数、邀请、模型、素材、预览、服务、余额恢复和贴纸清理各阶段纳入同事务 AdminOperationLog;实现 180 天 Worker 清理", + "parent_families": [ + "TDD-WP6-AUD-001" + ], + "case_ids": [ + "TDD-WP6-AUD-001-sensitive-operations" + ], + "first_red": [ + { + "family_id": "TDD-WP6-AUD-001", + "assertion": "敏感操作无日志、两类日志混用、含正文或 180 天内可删" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP6-AUD-001", + "assertion": "全部规定操作同事务审计;贴纸清理全阶段;两类表分离;到期 Worker 清理" + } + ], + "task_green_gate": "两类审计严格分离、180 天内不可改删、贴纸清理请求/复核/拒绝或排队/物理结果齐全且不含正文", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-admin, api, worker, sqlite.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "SQLite constraints, transactions, idempotency and side-effect assertions", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "DB", + "API", + "WRK", + "E2E" + ], + "validation_commands": [ + "pnpm test:integration", + "pnpm test:api", + "pnpm test:worker", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-AUD-001-sensitive-operations/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-6", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-audit", + "role": "implementation", + "frames": { + "frame_id": "DlCpN", + "frame_name": "06F 私有内容访问审计", + "primary_task": "TASK-WP6-04", + "mapped_state_scope": "两类独立审计、游标和 180 天;空/加载;查询失败;不可编辑删除;无正文、图片、密钥和路径" + }, + "applicable_states": { + "page_id": "admin-audit", + "actor_entry": "active super_admin", + "normal": "后台操作与私有访问两类独立页/标签", + "empty_loading": "无记录;游标加载", + "failure_disabled": "查询失败保留旧结果;无修改/人工删除动作", + "conflict_capacity_special": "180 天保留;正文、图片、密钥和路径不出现;页面读取不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-audit/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP6-05", + "work_package": "WP-6", + "title": "模型/服务/存储状态与诊断", + "dependencies": { + "source_text": "TASK-WP6-01、WP-0 诊断、WP-3 runtime", + "task_ids": [ + "TASK-WP6-01" + ], + "work_packages": [ + "WP-0", + "WP-3" + ] + }, + "requirements": [ + "ADMIN-03", + "ADMIN-08", + "GEN-15", + "NFR-09", + "PRIV-01", + "PRIV-02" + ], + "acceptance_criteria": [ + "AC-30", + "AC-41", + "AC-51", + "AC-55" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "GEN-15、ADMIN-03、08、AC-30、51", + "PRIV-01、02、ADMIN-08、NFR-09、AC-41、55" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "7.6 ModelConfig 原子配置与运行时推荐", + "9.6 运营后台与两类审计", + "11 外部服务硬停止与恢复" + ], + "modules": [ + "web-admin", + "api", + "sqlite", + "supervisor" + ], + "implementation_boundary": "实现总览、模型、服务与存储页面的 configured default/runtime/recommended 三列、Worker/网关/Resend/高德/storage 状态、REST refetch 和复制诊断", + "parent_families": [ + "TDD-WP6-DIAG-001", + "TDD-WP6-STATE-001" + ], + "case_ids": [ + "TDD-WP6-DIAG-001-redacted-diagnostics", + "TDD-WP6-STATE-001-three-model-states" + ], + "first_red": [ + { + "family_id": "TDD-WP6-DIAG-001", + "assertion": "总览/服务/存储/诊断暴露路径、密钥或正文" + }, + { + "family_id": "TDD-WP6-STATE-001", + "assertion": "后台把 configured default、runtime availability 和 recommended 合并" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP6-DIAG-001", + "assertion": "只显示固定非敏感字段、计量和状态;复制诊断通过同一脱敏扫描" + }, + { + "family_id": "TDD-WP6-STATE-001", + "assertion": "三列独立;临时异常不改配置;恢复只重新派生推荐" + } + ], + "task_green_gate": "推荐可 null;runtime 变化不回写配置;只显示稳定非敏感字段和逻辑位置;无密钥、邮箱、路径、正文或供应商原文", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: web-admin, api, sqlite, supervisor.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "OpenAPI, error envelope and HTTP semantics", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "API", + "E2E", + "PKG-SEC" + ], + "validation_commands": [ + "pnpm test:api", + "pnpm test:e2e", + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-DIAG-001-redacted-diagnostics/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP6-STATE-001-three-model-states/" + ], + "automation": [ + "automated" + ], + "release_gate": [ + "work_package:WP-6", + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "admin-overview", + "role": "implementation", + "frames": { + "frame_id": "e4e2Kz", + "frame_name": "06A 后台总览", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "名额、任务、成本核对、服务/容量和审计摘要;骨架/空事件;聚合失败;三模型状态分列和待处理事项" + }, + "applicable_states": { + "page_id": "admin-overview", + "actor_entry": "active super_admin", + "normal": "名额、任务、模型、点数、服务、容量、审计摘要", + "empty_loading": "无近期事件;指标骨架", + "failure_disabled": "聚合失败保留旧值和时间;敏感动作按状态禁用", + "conflict_capacity_special": "default/runtime/recommended 分列;pending_manual_review 为成本核对;容量持续条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-overview/\u003cstate\u003e/" + }, + { + "page_id": "admin-models", + "role": "implementation", + "frames": { + "frame_id": "YVfVG", + "frame_name": "06B 模型配置", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "完整三模型集合、default/runtime/recommended;加载;空推荐;替代/priority 禁用;CAS 冲突和整套回滚" + }, + "applicable_states": { + "page_id": "admin-models", + "actor_entry": "active super_admin", + "normal": "完整三模型集合、default/runtime/recommended 三层", + "empty_loading": "初始加载骨架;无 recommended 是合法状态", + "failure_disabled": "合法替代/priority 前置禁用;契约未验证/运行时不可用", + "conflict_capacity_special": "config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-models/\u003cstate\u003e/" + }, + { + "page_id": "admin-services-storage", + "role": "implementation", + "frames": { + "frame_id": "L3lde2", + "frame_name": "06E 服务与存储", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "Resend/高德/网关/Worker/LocalDataRoot;指标加载;paused/degraded/unavailable;恢复前置、清理排队和重计量" + }, + "applicable_states": { + "page_id": "admin-services-storage", + "actor_entry": "active super_admin", + "normal": "Resend/高德/网关/Worker/LocalDataRoot 状态与允许动作", + "empty_loading": "指标加载;无告警", + "failure_disabled": "paused/degraded/unavailable;不合法恢复/提高上限禁用", + "conflict_capacity_special": "full/unavailable、清理排队和物理重计量;不显示绝对路径" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-services-storage/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP7-01", + "work_package": "WP-7", + "title": "候选制品与候选浏览器记录", + "dependencies": { + "source_text": "WP-0 至 WP-6 Green", + "task_ids": [ + + ], + "work_packages": [ + "WP-0", + "WP-6" + ] + }, + "requirements": [ + "NFR-01", + "NFR-09" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41" + ], + "acceptance_criteria_role": "supporting", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-01、NFR-09、AC-24、AC-41;候选记录支撑,不新增测试 case" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.1 便携包布局", + "3.6 浏览器支持硬门禁", + "15 WP-7" + ], + "modules": [ + "release-ci", + "supervisor", + "web", + "api", + "worker" + ], + "implementation_boundary": "从候选便携包和当前安装的真实 Chrome/Edge 可执行文件生成 artifacts/tdd/\u003crun_id\u003e/release-candidate.json,记录 Windows build、品牌、major、full version、记录时间、候选制品哈希和固定端口;生成候选 ZIP 与候选 START-HERE.txt,但不写最终 RELEASE.json", + "parent_families": [ + + ], + "case_ids": [ + + ], + "first_red": [ + + ], + "minimal_green": [ + + ], + "task_green_gate": "候选记录字段完整、脱敏且与候选制品一致;只作为后续精确环境输入,不能标记发布冻结或替代最终 RELEASE 证据", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-ci, supervisor, web, api, worker.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "credential, path, log, package and diagnostic redaction boundaries", + "candidate record must remain distinct from final RELEASE.json" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "PKG-SEC", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:security", + "pnpm test:package", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/release-candidate.json", + "artifacts/tdd/\u003crun_id\u003e/candidate-package/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP7-02", + "work_package": "WP-7", + "title": "三模型真实契约验收", + "dependencies": { + "source_text": "TASK-WP7-01、WP-3 Green、受控凭据", + "task_ids": [ + "TASK-WP7-01" + ], + "work_packages": [ + "WP-3" + ] + }, + "requirements": [ + "GEN-13" + ], + "acceptance_criteria": [ + "AC-40", + "AC-41" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "GEN-13、AC-40、41" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "8.3 契约验证证据", + "15 WP-7" + ], + "modules": [ + "release-validation", + "ai-adapters", + "windows-credential-manager" + ], + "implementation_boundary": "对三个模型分别运行纯文字、参考图、四比例、同步/异步、响应、九类错误、结算和契约变化重验,保存脱敏证据", + "parent_families": [ + "TDD-WP7-EXT-001" + ], + "case_ids": [ + "TDD-WP7-EXT-001-three-real-models" + ], + "first_red": [ + { + "family_id": "TDD-WP7-EXT-001", + "assertion": "任一模型缺独立真实契约证据" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-EXT-001", + "assertion": "三模型各自完成纯文字、参考图、四比例、同步/异步、响应、九类错误和结算证据" + } + ], + "task_green_gate": "每个模型独立证据齐全且匹配最后配置;任一失败保持 externally_blocked;不共享 verified 标记", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-validation, ai-adapters, windows-credential-manager.", + "must_preserve": [ + "PRD product semantics and P0-A boundary" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "EXT-REAL", + "MANUAL" + ], + "validation_commands": [ + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gemini-3.1-flash-image-preview", + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gemini-3-pro-image-preview", + "pnpm validate:external -- --service \u003cai-gateway-service-id\u003e --model gpt-image-2", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-EXT-001-three-real-models/" + ], + "automation": [ + "controlled_real", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP7-03", + "work_package": "WP-7", + "title": "Resend 真实发布门槛", + "dependencies": { + "source_text": "TASK-WP7-01、WP-1/WP-6 Green、受控域名", + "task_ids": [ + "TASK-WP7-01" + ], + "work_packages": [ + "WP-1", + "WP-6" + ] + }, + "requirements": [ + "AUTH-01", + "AUTH-02", + "AUTH-07" + ], + "acceptance_criteria": [ + "AC-01", + "AC-33", + "AC-41", + "AC-47", + "AC-49" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "AUTH-01、02、07、AC-01、33、41、47、49" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "1.4 外部产品发布阻塞与可并行范围", + "11.1 Resend", + "15 WP-7" + ], + "modules": [ + "release-validation", + "resend-adapter", + "windows-credential-manager" + ], + "implementation_boundary": "核验 SPF/DKIM、免费规则;QQ/163/企业邮箱各 20 封至少 19 封在 2 分钟内送达;用正式链完成普通/管理员认证", + "parent_families": [ + "TDD-WP7-EXT-002" + ], + "case_ids": [ + "TDD-WP7-EXT-002-real-resend" + ], + "first_red": [ + { + "family_id": "TDD-WP7-EXT-002", + "assertion": "Resend 域名/免费规则/送达门槛未证实" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-EXT-002", + "assertion": "依据 PRD 第 23.2 节、AC-41、AC-47 与 DevelopmentPlan 第 1.4、11.1 节:SPF/DKIM;QQ、163、企业邮箱各 20 封至少 19 封在 2 分钟内送达;正式账号真实认证" + } + ], + "task_green_gate": "人工统计脱敏;开发预置账号和 mock 不能作为证据;未达门槛不启动实际 P0-A 验收", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-validation, resend-adapter, windows-credential-manager.", + "must_preserve": [ + "PRD product semantics and P0-A boundary" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "EXT-REAL", + "MANUAL" + ], + "validation_commands": [ + "pnpm validate:external -- --service \u003cresend-service-id\u003e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-EXT-002-real-resend/" + ], + "automation": [ + "controlled_real", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "user-auth", + "role": "final_evidence", + "frames": [ + { + "frame_id": "Z0pf8", + "frame_name": "01A 普通用户登录", + "primary_task": "TASK-WP1-02", + "mapped_state_scope": "登录正常;字段初始空;发码/登录加载;验证码、Resend 和身份状态失败;管理员入口、固定本机提示、艺术带与焦点" + }, + { + "frame_id": "DVPM8", + "frame_name": "01B 注册展开态", + "primary_task": "TASK-WP1-03", + "mapped_state_scope": "邀请与邮箱已验证摘要;资料和告知未完成/完成;注册中;名额、邀请、验证码和提交失败;主动作禁用" + } + ], + "applicable_states": { + "page_id": "user-auth", + "actor_entry": "未登录普通入口", + "normal": "登录、分阶段注册、管理员文字入口、固定本机提示", + "empty_loading": "字段初始空;发码/验证/注册加载", + "failure_disabled": "Resend、验证码、邀请、名额、告知未完成;按钮禁用", + "conflict_capacity_special": "N/A:认证不消费存储容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/user-auth/\u003cstate\u003e/" + }, + { + "page_id": "admin-auth", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP1-04", + "mapped_state_scope": "白名单邮箱验证码、字段空/加载、非白名单、停用/移除、Resend 失败;独立 audience 与返回普通登录" + }, + "applicable_states": { + "page_id": "admin-auth", + "actor_entry": "未登录后台入口", + "normal": "白名单邮箱验证码、独立 audience、返回普通登录", + "empty_loading": "字段空;发码/登录中", + "failure_disabled": "非白名单发码前阻断、停用/移除白名单、Resend 错误", + "conflict_capacity_special": "N/A:不显示普通创作或容量业务" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-auth/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP7-04", + "work_package": "WP-7", + "title": "高德真实发布门槛", + "dependencies": { + "source_text": "TASK-WP7-01、WP-4/WP-6 Green、受控 Key", + "task_ids": [ + "TASK-WP7-01" + ], + "work_packages": [ + "WP-4", + "WP-6" + ] + }, + "requirements": [ + "DYN-04", + "PRIV-03" + ], + "acceptance_criteria": [ + "AC-17", + "AC-41", + "AC-47" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "DYN-04、PRIV-03、AC-17、41、47" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "1.4 外部产品发布阻塞与可并行范围", + "11.2 高德", + "15 WP-7" + ], + "modules": [ + "release-validation", + "amap-adapter", + "windows-credential-manager" + ], + "implementation_boundary": "核验定位/逆地理、QPS、白名单、安全限制、每月 1,000 次硬停止和 DYN004 确认流程", + "parent_families": [ + "TDD-WP7-EXT-003" + ], + "case_ids": [ + "TDD-WP7-EXT-003-real-amap" + ], + "first_red": [ + { + "family_id": "TDD-WP7-EXT-003", + "assertion": "高德接口、绑定、安全限制或 1,000 次硬上限未证实" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-EXT-003", + "assertion": "定位/逆地理、QPS、白名单、安全和应用硬停止实测通过" + } + ], + "task_green_gate": "真实测试坐标不关联真实项目;不发第 1001 次;不启用付费或自动扩容;失败保持 externally_blocked", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-validation, amap-adapter, windows-credential-manager.", + "must_preserve": [ + "PRD product semantics and P0-A boundary" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "EXT-REAL", + "MANUAL" + ], + "validation_commands": [ + "pnpm validate:external -- --service \u003camap-service-id\u003e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-EXT-003-real-amap/" + ], + "automation": [ + "controlled_real", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP7-05", + "work_package": "WP-7", + "title": "候选精确环境 UI、视觉、性能与可访问性验收", + "dependencies": { + "source_text": "TASK-WP7-01 至 TASK-WP7-04", + "task_ids": [ + "TASK-WP7-01", + "TASK-WP7-04" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-02", + "NFR-06" + ], + "acceptance_criteria": [ + "AC-24", + "AC-25", + "AC-32", + "AC-41", + "AC-55", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "UIDesign 全页、NFR-02、06、AC-24、25、32、41、55、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.6 浏览器支持硬门禁", + "13 性能、可访问性与视觉一致性", + "15 WP-7" + ], + "modules": [ + "release-validation", + "web", + "web-editor", + "supervisor" + ], + "implementation_boundary": "严格使用候选记录中的两个真实 full version,在 1920×1080、DPR 1、100%/200% 下重跑 AC-24、AC-32、全部 E2E、视觉、性能、下载、文件选择和逐个非 N/A 页面状态;按 frame 对照低保真", + "parent_families": [ + "TDD-WP7-UI-001" + ], + "case_ids": [ + "TDD-WP7-UI-001-page-state-evidence" + ], + "first_red": [ + { + "family_id": "TDD-WP7-UI-001", + "assertion": "UIDesign 页面/入口或适用状态无实现落点,或 1080p/200% 出现遮挡" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-UI-001", + "assertion": "第 11.1 节页面 × 状态矩阵全部适用单元格有证据;Pencil 仅作布局参考,Canvas Index 不计产品实现;固定文案、焦点、状态、控件和无重叠检查通过" + } + ], + "task_green_gate": "22 个 coverage unit 全部有证据;候选环境与记录任何漂移均使本任务重跑;无独立 Pencil frame 的页面不漏测;Canvas Index 不计产品页", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-validation, web, web-editor, supervisor.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "E2E", + "VIS-PERF", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:e2e", + "pnpm test:visual", + "pnpm test:performance", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-UI-001-page-state-evidence/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "support-gate", + "role": "final_evidence", + "frames": { + "frame_id": "u1adni", + "frame_name": "07A 浏览器硬阻断", + "primary_task": "TASK-WP0-03", + "mapped_state_scope": "四种阻断 reason、支持品牌/major、最小诊断;bundle/API 无绕过;无业务空态、身份或容量读取" + }, + "applicable_states": { + "page_id": "support-gate", + "actor_entry": "所有主体,产品门禁前", + "normal": "四种阻断 reason、支持品牌/major、无继续入口", + "empty_loading": "N/A:引导无业务空态;身份不可判定属于阻断", + "failure_disabled": "bundle/API 均无法绕过;诊断仅最小字段", + "conflict_capacity_special": "N/A:不读取业务状态或容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/support-gate/\u003cstate\u003e/" + }, + { + "page_id": "user-auth", + "role": "final_evidence", + "frames": [ + { + "frame_id": "Z0pf8", + "frame_name": "01A 普通用户登录", + "primary_task": "TASK-WP1-02", + "mapped_state_scope": "登录正常;字段初始空;发码/登录加载;验证码、Resend 和身份状态失败;管理员入口、固定本机提示、艺术带与焦点" + }, + { + "frame_id": "DVPM8", + "frame_name": "01B 注册展开态", + "primary_task": "TASK-WP1-03", + "mapped_state_scope": "邀请与邮箱已验证摘要;资料和告知未完成/完成;注册中;名额、邀请、验证码和提交失败;主动作禁用" + } + ], + "applicable_states": { + "page_id": "user-auth", + "actor_entry": "未登录普通入口", + "normal": "登录、分阶段注册、管理员文字入口、固定本机提示", + "empty_loading": "字段初始空;发码/验证/注册加载", + "failure_disabled": "Resend、验证码、邀请、名额、告知未完成;按钮禁用", + "conflict_capacity_special": "N/A:认证不消费存储容量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/user-auth/\u003cstate\u003e/" + }, + { + "page_id": "workspace", + "role": "final_evidence", + "frames": [ + { + "frame_id": "WbDYT", + "frame_name": "02A 空工作台", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "无项目艺术带;生成表单、推荐模型、四比例与空任务;模型/项目加载;模型、点数、参考图和容量禁用" + }, + { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + } + ], + "applicable_states": { + "page_id": "workspace", + "actor_entry": "active 普通/内部预览账号", + "normal": "生成表单、模型/点数/比例、最近项目", + "empty_loading": "无项目艺术带;模型/项目加载", + "failure_disabled": "服务/参考图/生成错误;模型、点数、单任务、历史/项目上限禁用", + "conflict_capacity_special": "model config 变化重确认;warning/critical 提示;full/unavailable 阻断新增" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/workspace/\u003cstate\u003e/" + }, + { + "page_id": "current-task", + "role": "final_evidence", + "frames": { + "frame_id": "d99Lo", + "frame_name": "02B 生成中工作台", + "primary_task": "TASK-WP2-05", + "mapped_state_scope": "queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认" + }, + "applicable_states": { + "page_id": "current-task", + "actor_entry": "active 普通/内部预览账号", + "normal": "真实状态、结果入口", + "empty_loading": "无活动任务;queued/running 无假百分比", + "failure_disabled": "failed/rejected 与九类固定动作;第二任务禁用", + "conflict_capacity_special": "storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/current-task/\u003cstate\u003e/" + }, + { + "page_id": "projects", + "role": "final_evidence", + "frames": { + "frame_id": "zXyG9", + "frame_name": "03A 项目列表", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "active/trashed、筛选、多选 failed_empty;空/骨架;20 active、恢复/批量禁用;多标签和容量提示" + }, + "applicable_states": { + "page_id": "projects", + "actor_entry": "active 普通/内部预览账号", + "normal": "active/trashed 列表、筛选、多选 failed_empty", + "empty_loading": "无项目/筛选空;稳定卡片骨架", + "failure_disabled": "列表失败保留旧数据;20 active、恢复/批量条件禁用", + "conflict_capacity_special": "多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/projects/\u003cstate\u003e/" + }, + { + "page_id": "project-detail", + "role": "final_evidence", + "frames": { + "frame_id": "v2Qbu", + "frame_name": "03B 项目详情", + "primary_task": "TASK-WP2-01", + "mapped_state_scope": "历史、固定比例、继续生成、编辑、原图/latest;无成功图/无成品;10 图上限、资源失败和容量状态" + }, + "applicable_states": { + "page_id": "project-detail", + "actor_entry": "项目所有者", + "normal": "历史、固定比例、继续生成、编辑、原图/latest 下载", + "empty_loading": "无成功图草稿;历史/成品加载", + "failure_disabled": "10 图上限、当前底图不可删、无 latest、资源读取失败", + "conflict_capacity_special": "比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/project-detail/\u003cstate\u003e/" + }, + { + "page_id": "editor", + "role": "final_evidence", + "frames": { + "frame_id": "bHQnJ", + "frame_name": "04A 三栏编辑器", + "primary_task": "TASK-WP4-01", + "mapped_state_scope": "三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable" + }, + "applicable_states": { + "page_id": "editor", + "actor_entry": "有成功底图的项目所有者", + "normal": "三栏、保存状态、画布、属性与素材", + "empty_loading": "画布/素材加载;无成功图不能正常进入", + "failure_disabled": "保存/素材失败;第 51 元素、不可用模板禁用", + "conflict_capacity_special": "dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/editor/\u003cstate\u003e/" + }, + { + "page_id": "export", + "role": "final_evidence", + "frames": { + "frame_id": "i9upg7", + "frame_name": "04B 导出弹层", + "primary_task": "TASK-WP4-05", + "mapped_state_scope": "JPG/PNG、质量、尺寸;合成/持久化加载;未提交确认;四类失败结果;冲突一次下载和 full/unavailable" + }, + "applicable_states": { + "page_id": "export", + "actor_entry": "编辑器内项目所有者", + "normal": "JPG/PNG、质量、尺寸、下载/latest 分结果", + "empty_loading": "合成/持久化加载;无业务空态", + "failure_disabled": "合成、下载、项目保存、latest 保存分别失败;无画布/冲突次数用尽禁用", + "conflict_capacity_special": "未提交确认;conflicted 仅一次本地导出;full/unavailable 后端失败不撤回下载" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/export/\u003cstate\u003e/" + }, + { + "page_id": "credits", + "role": "final_evidence", + "frames": { + "frame_id": "YlKzE", + "frame_name": "05A 点数明细", + "primary_task": "TASK-WP2-04", + "mapped_state_scope": "available/reserved、流水和分页;无流水/加载;失败保留安全摘要;负 available 如实显示且无购买入口" + }, + "applicable_states": { + "page_id": "credits", + "actor_entry": "active 普通/内部预览账号", + "normal": "available/reserved、流水、关联与时间", + "empty_loading": "无流水;分页加载", + "failure_disabled": "加载失败保留安全余额摘要", + "conflict_capacity_special": "负 available 如实显示;无购买/充值入口;页面操作不消费容量但仍继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/credits/\u003cstate\u003e/" + }, + { + "page_id": "settings", + "role": "final_evidence", + "frames": { + "frame_id": "Ws5E0", + "frame_name": "05B 设置与本机数据", + "primary_task": "TASK-WP1-05", + "mapped_state_scope": "资料、本机数据、容量摘要和固定文案;加载/保存失败;full/unavailable;注销确认、处理中和返回登录" + }, + "applicable_states": { + "page_id": "settings", + "actor_entry": "active 普通/内部预览账号", + "normal": "资料、本机数据、容量摘要、固定文案、注销", + "empty_loading": "资料/状态加载", + "failure_disabled": "保存失败;注销确认/处理中禁用重复提交", + "conflict_capacity_special": "full/unavailable 状态与允许动作;注销后返回登录" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/settings/\u003cstate\u003e/" + }, + { + "page_id": "preview-user-variant", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "有效授权、无授权/空批次、加载、撤销/过期阻断;不出现后台权限;全局容量条" + }, + "applicable_states": { + "page_id": "preview-user-variant", + "actor_entry": "有有效/撤销/过期 AssetPreviewGrant 的普通账号", + "normal": "授权批次素材显示“预览”,其余页面同普通用户", + "empty_loading": "无授权/批次空;manifest 加载", + "failure_disabled": "撤销/过期立即阻断新读取", + "conflict_capacity_special": "不出现后台入口、调点、邀请码、模型或私有内容;继承普通端全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/preview-user-variant/\u003cstate\u003e/" + }, + { + "page_id": "admin-auth", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP1-04", + "mapped_state_scope": "白名单邮箱验证码、字段空/加载、非白名单、停用/移除、Resend 失败;独立 audience 与返回普通登录" + }, + "applicable_states": { + "page_id": "admin-auth", + "actor_entry": "未登录后台入口", + "normal": "白名单邮箱验证码、独立 audience、返回普通登录", + "empty_loading": "字段空;发码/登录中", + "failure_disabled": "非白名单发码前阻断、停用/移除白名单、Resend 错误", + "conflict_capacity_special": "N/A:不显示普通创作或容量业务" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-auth/\u003cstate\u003e/" + }, + { + "page_id": "admin-overview", + "role": "final_evidence", + "frames": { + "frame_id": "e4e2Kz", + "frame_name": "06A 后台总览", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "名额、任务、成本核对、服务/容量和审计摘要;骨架/空事件;聚合失败;三模型状态分列和待处理事项" + }, + "applicable_states": { + "page_id": "admin-overview", + "actor_entry": "active super_admin", + "normal": "名额、任务、模型、点数、服务、容量、审计摘要", + "empty_loading": "无近期事件;指标骨架", + "failure_disabled": "聚合失败保留旧值和时间;敏感动作按状态禁用", + "conflict_capacity_special": "default/runtime/recommended 分列;pending_manual_review 为成本核对;容量持续条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-overview/\u003cstate\u003e/" + }, + { + "page_id": "admin-users", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "脱敏列表/详情、暂停恢复/调点、空/筛选空/骨架、原因缺失和状态禁用、负 available" + }, + "applicable_states": { + "page_id": "admin-users", + "actor_entry": "active super_admin", + "normal": "脱敏列表/详情、暂停恢复、调点", + "empty_loading": "无用户/筛选空;表格骨架", + "failure_disabled": "操作失败、原因缺失、状态前置禁用", + "conflict_capacity_special": "调点可使 available 为负但 reserved 不变;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-users/\u003cstate\u003e/" + }, + { + "page_id": "admin-invites", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP6-01", + "mapped_state_scope": "创建/停用/期限/次数/脱敏复制、空/骨架、名额满、过期/耗尽、请求失败和幂等/CAS 结果" + }, + "applicable_states": { + "page_id": "admin-invites", + "actor_entry": "active super_admin", + "normal": "创建、停用、期限/次数、脱敏复制", + "empty_loading": "无邀请码;表格骨架", + "failure_disabled": "名额满、停用/过期/耗尽不可当可用;请求失败", + "conflict_capacity_special": "CAS/幂等结果;页面操作不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-invites/\u003cstate\u003e/" + }, + { + "page_id": "admin-models", + "role": "final_evidence", + "frames": { + "frame_id": "YVfVG", + "frame_name": "06B 模型配置", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "完整三模型集合、default/runtime/recommended;加载;空推荐;替代/priority 禁用;CAS 冲突和整套回滚" + }, + "applicable_states": { + "page_id": "admin-models", + "actor_entry": "active super_admin", + "normal": "完整三模型集合、default/runtime/recommended 三层", + "empty_loading": "初始加载骨架;无 recommended 是合法状态", + "failure_disabled": "合法替代/priority 前置禁用;契约未验证/运行时不可用", + "conflict_capacity_special": "config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-models/\u003cstate\u003e/" + }, + { + "page_id": "admin-assets", + "role": "final_evidence", + "frames": { + "frame_id": "A28V3c", + "frame_name": "06C 素材与历史清理", + "primary_task": "TASK-WP5-06", + "mapped_state_scope": "上传/版本/候选;无上传/无候选;格式/编译/权限失败;full/unavailable;引用冲突、排队、物理删除和重计量" + }, + "applicable_states": { + "page_id": "admin-assets", + "actor_entry": "active super_admin", + "normal": "普通贴纸、资源版本/发布、历史清理", + "empty_loading": "无上传素材/无候选;列表/处理加载", + "failure_disabled": "格式/编译失败;权限、文件状态禁用", + "conflict_capacity_special": "stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-assets/\u003cstate\u003e/" + }, + { + "page_id": "admin-preview", + "role": "final_evidence", + "frames": { + "frame_id": "UIDesign-only", + "frame_name": "无独立画板", + "primary_task": "TASK-WP5-07", + "mapped_state_scope": "批次、模板状态、授权/撤销/到期、空/骨架、非法转换/发布失败;不出现 P0-B 全量开放" + }, + "applicable_states": { + "page_id": "admin-preview", + "actor_entry": "active super_admin", + "normal": "批次、模板状态、账号授权/撤销/到期", + "empty_loading": "无批次/授权;表格骨架", + "failure_disabled": "非法状态转换、过期、发布校验失败", + "conflict_capacity_special": "授权不改变角色;P0-B 全量开放不可在 P0-A 出现;继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-preview/\u003cstate\u003e/" + }, + { + "page_id": "admin-generations", + "role": "final_evidence", + "frames": { + "frame_id": "OnsiT", + "frame_name": "06D 生成记录与私有告知", + "primary_task": "TASK-WP6-02", + "mapped_state_scope": "默认隐藏正文;身份+版本告知;逐次打开;空记录/审计写入加载;428、审计失败、升版重阻断" + }, + "applicable_states": { + "page_id": "admin-generations", + "actor_entry": "active super_admin", + "normal": "默认安全摘要、告知、逐次打开", + "empty_loading": "无记录;列表/审计写入加载", + "failure_disabled": "未确认 428、审计失败不打开、权限禁用", + "conflict_capacity_special": "告知升版重阻断;每次打开先独立审计;页面读取不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-generations/\u003cstate\u003e/" + }, + { + "page_id": "admin-services-storage", + "role": "final_evidence", + "frames": { + "frame_id": "L3lde2", + "frame_name": "06E 服务与存储", + "primary_task": "TASK-WP6-05", + "mapped_state_scope": "Resend/高德/网关/Worker/LocalDataRoot;指标加载;paused/degraded/unavailable;恢复前置、清理排队和重计量" + }, + "applicable_states": { + "page_id": "admin-services-storage", + "actor_entry": "active super_admin", + "normal": "Resend/高德/网关/Worker/LocalDataRoot 状态与允许动作", + "empty_loading": "指标加载;无告警", + "failure_disabled": "paused/degraded/unavailable;不合法恢复/提高上限禁用", + "conflict_capacity_special": "full/unavailable、清理排队和物理重计量;不显示绝对路径" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-services-storage/\u003cstate\u003e/" + }, + { + "page_id": "admin-audit", + "role": "final_evidence", + "frames": { + "frame_id": "DlCpN", + "frame_name": "06F 私有内容访问审计", + "primary_task": "TASK-WP6-04", + "mapped_state_scope": "两类独立审计、游标和 180 天;空/加载;查询失败;不可编辑删除;无正文、图片、密钥和路径" + }, + "applicable_states": { + "page_id": "admin-audit", + "actor_entry": "active super_admin", + "normal": "后台操作与私有访问两类独立页/标签", + "empty_loading": "无记录;游标加载", + "failure_disabled": "查询失败保留旧结果;无修改/人工删除动作", + "conflict_capacity_special": "180 天保留;正文、图片、密钥和路径不出现;页面读取不消费容量但仍继承后台全局容量条" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/admin-audit/\u003cstate\u003e/" + }, + { + "page_id": "system-ui", + "role": "final_evidence", + "frames": { + "frame_id": "awt96", + "frame_name": "07B Windows 辅助系统界面", + "primary_task": "TASK-WP0-07", + "mapped_state_scope": "托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作" + }, + "applicable_states": { + "page_id": "system-ui", + "actor_entry": "本机托盘/Supervisor,无产品角色", + "normal": "托盘、运行状态、打开 Dada、诊断、退出", + "empty_loading": "启动中;N/A 业务空态", + "failure_disabled": "启动失败、端口占用、API/Worker degraded,动作严格受限", + "conflict_capacity_special": "full/unavailable 只作脱敏告警;固定端口不动态切换" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/system-ui/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + }, + { + "task_id": "TASK-WP7-06", + "work_package": "WP-7", + "title": "追踪闭环与最终 AC 预冻结验收", + "dependencies": { + "source_text": "TASK-WP7-01 至 TASK-WP7-05", + "task_ids": [ + "TASK-WP7-01", + "TASK-WP7-05" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-05" + ], + "acceptance_criteria": [ + "AC-01", + "AC-02", + "AC-03", + "AC-04", + "AC-05", + "AC-06", + "AC-07", + "AC-09", + "AC-10", + "AC-11", + "AC-12", + "AC-13", + "AC-14", + "AC-15", + "AC-16", + "AC-17", + "AC-18", + "AC-19", + "AC-20", + "AC-21", + "AC-22", + "AC-23", + "AC-24", + "AC-25", + "AC-27", + "AC-28", + "AC-29", + "AC-30", + "AC-31", + "AC-32", + "AC-33", + "AC-34", + "AC-35", + "AC-36", + "AC-38", + "AC-39", + "AC-40", + "AC-41", + "AC-42", + "AC-43", + "AC-44", + "AC-45", + "AC-46", + "AC-47", + "AC-48", + "AC-49", + "AC-50", + "AC-51", + "AC-52", + "AC-53", + "AC-55", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-05、52 当前 AC、109 需求 ID、13 模块、19 契约、9 错误" + ], + "acceptance_scope_notes": [ + "NFR-05、52 当前 AC、109 需求 ID、13 模块、19 契约、9 错误" + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "14 测试、监控与 AC 追踪", + "15 WP-7", + "19 完成定义" + ], + "modules": [ + "release-validation", + "trace-validator" + ], + "implementation_boundary": "运行 test:all 和 validate:tdd-trace;汇总候选制品、外部、UI/视觉/性能证据;用固定 super_admin/普通账号及正常链创建临时第二普通账号完成 52 AC;确认候选浏览器字段在验收期间未漂移", + "parent_families": [ + "TDD-WP7-AC-001" + ], + "case_ids": [ + "TDD-WP7-AC-001-trace-structure" + ], + "first_red": [ + { + "family_id": "TDD-WP7-AC-001", + "assertion": "52 项 AC、89 个测试族或 117 个规范 case 缺失,重复主责、孤儿 case 或用 P0-B 结果冒充" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-AC-001", + "assertion": "第 12 节矩阵每项一个主责且证据齐全;89/89 父族有 case 且派生属性完整;未来四项不计入;四份全局来源哈希未变,UI 参考变化按第 1.2.2 节局部重验" + } + ], + "task_green_gate": "52/52 当前 AC passed;109 需求 ID、13 模块、19 契约、9 错误、89/89 父族、117/117 case 完整;未来四项不计入;未通过时不得写最终 RELEASE.json", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-validation, trace-validator.", + "must_preserve": [ + "PRD product semantics and P0-A boundary" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "UNIT", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:unit", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-AC-001-trace-structure/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + + ], + "ui_scope_reason": "No direct UI coverage-unit ownership; validate only the listed module and case boundaries." + }, + { + "task_id": "TASK-WP7-07", + "work_package": "WP-7", + "title": "最终 RELEASE 冻结、制品生成与泄漏扫描", + "dependencies": { + "source_text": "TASK-WP7-06", + "task_ids": [ + "TASK-WP7-06" + ], + "work_packages": [ + + ] + }, + "requirements": [ + "NFR-01", + "NFR-09", + "PRIV-01", + "PRIV-02" + ], + "acceptance_criteria": [ + "AC-24", + "AC-41", + "AC-48", + "AC-56" + ], + "acceptance_criteria_role": "direct-or-supporting-as-listed-by-normative-cases", + "product_contracts": [ + + ], + "requirement_ac_sources": [ + "NFR-01、09、AC-24、41", + "PRIV-01、02、NFR-09、AC-41、48、56" + ], + "acceptance_scope_notes": [ + + ], + "fixture_ids": [ + + ], + "development_plan_sections": [ + "3.1 便携包布局", + "3.6 浏览器支持硬门禁", + "12.3 审计与敏感信息", + "15 WP-7", + "19 完成定义" + ], + "modules": [ + "release-ci", + "trace-validator", + "security-scanner", + "supervisor", + "web", + "api", + "worker" + ], + "implementation_boundary": "验收通过后把未漂移的候选浏览器记录写入最终 RELEASE.json,生成最终 ZIP、SHA-256 和 START-HERE.txt;把 RELEASE SHA-256 写入最终 evidence.json;扫描源码、Git、最终 ZIP、DB 配置、manifest、日志、截图、trace/evidence,并执行最终 package/support-gate smoke", + "parent_families": [ + "TDD-WP7-REL-001", + "TDD-WP7-SEC-001" + ], + "case_ids": [ + "TDD-WP7-REL-001-final-release-record", + "TDD-WP7-SEC-001-artifact-leak-scan" + ], + "first_red": [ + { + "family_id": "TDD-WP7-REL-001", + "assertion": "RELEASE.json 缺失/漂移、ZIP 哈希或真实浏览器未验收" + }, + { + "family_id": "TDD-WP7-SEC-001", + "assertion": "制品、日志、DB 配置、manifest 或文档出现真实敏感值/用户路径" + } + ], + "minimal_green": [ + { + "family_id": "TDD-WP7-REL-001", + "assertion": "发布记录、制品、START-HERE、Chrome/Edge full version、固定 major 和 Windows build 一致" + }, + { + "family_id": "TDD-WP7-SEC-001", + "assertion": "全制品扫描零命中;凭据只在 Windows Credential Manager;诊断脱敏" + } + ], + "task_green_gate": "最终记录、制品、Windows build、两个 full version、固定 major 和端口一致;零真实敏感值/用户绝对路径;最终写入或扫描失败时不得发布,且不能沿用候选 passed 状态冒充最终冻结", + "refactor_scope": { + "allowed": "Only internal deduplication and structural improvement inside modules: release-ci, trace-validator, security-scanner, supervisor, web, api, worker.", + "must_preserve": [ + "PRD product semantics and P0-A boundary", + "UIDesign page states, message_key actions, focus, layout and visual/performance thresholds", + "credential, path, log, package and diagnostic redaction boundaries" + ], + "forbidden": [ + "new product routes, roles, states, fallback behavior or configuration", + "technology replacement or cross-module ownership change", + "weakening or deleting any assigned case assertion" + ] + }, + "layers": [ + "PKG-SEC", + "E2E", + "MANUAL" + ], + "validation_commands": [ + "pnpm test:security", + "pnpm test:package", + "pnpm test:e2e", + "pnpm validate:tdd-trace" + ], + "evidence_directories": [ + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-REL-001-final-release-record/", + "artifacts/tdd/\u003crun_id\u003e/cases/TDD-WP7-SEC-001-artifact-leak-scan/" + ], + "automation": [ + "automated", + "manual_review" + ], + "release_gate": [ + "release:P0-A" + ], + "ui_scope": [ + { + "page_id": "system-ui", + "role": "final_evidence", + "frames": { + "frame_id": "awt96", + "frame_name": "07B Windows 辅助系统界面", + "primary_task": "TASK-WP0-07", + "mapped_state_scope": "托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作" + }, + "applicable_states": { + "page_id": "system-ui", + "actor_entry": "本机托盘/Supervisor,无产品角色", + "normal": "托盘、运行状态、打开 Dada、诊断、退出", + "empty_loading": "启动中;N/A 业务空态", + "failure_disabled": "启动失败、端口占用、API/Worker degraded,动作严格受限", + "conflict_capacity_special": "full/unavailable 只作脱敏告警;固定端口不动态切换" + }, + "evidence_directory": "artifacts/tdd/\u003crun_id\u003e/ui/system-ui/\u003cstate\u003e/" + } + ], + "ui_scope_reason": null + } + ], + "case_catalog": [ + { + "case_id": "TDD-WP4-TXT-002-font-metrics-ranges", + "parent_family": "TDD-WP4-TXT-002", + "requirement_ac_source": "TEXT-07 至 14、NFR-02、AC-11、12", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "真实归档字体/缺失字体、最长文字、属性边界/步长", + "numbered_steps": "1. 等待 FontFace ready;2. 参数化字号/缩放/行距/字距/描边/颜色/背景透明度;3. 完成;4. 缺字体加载;5. 重开/导出", + "expected_response_ui": "字号与控制柄同步,重新测量不裁切;范围步长精确;缺字体模板 unavailable;背景透明度不影响文字", + "expected_db_files": "完成一次撤销/保存;缺字体分支无 Canvas 新元素", + "forbidden_side_effects": "不静默系统字体回退、不把背景透明度应用文字、不超范围取值", + "evidence_files": "font-load.json、canvas-state.json、pixel-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP2-PROJ-004-stale-tab", + "parent_family": "TDD-WP2-PROJ-004", + "requirement_ac_source": "PROJECT-07、AC-36 P0-A 子集", + "fixture_ids": [ + "FX-PROJECT-CONFLICT" + ], + "fixture_and_preconditions": "FX-PROJECT-CONFLICT,标签 A/B 均基于 N", + "numbered_steps": "1. A 保存到 N+1;2. B 以 N 保存;3. B 尝试编辑/覆盖/另存副本", + "expected_response_ui": "B 返回 412 和最新安全版本摘要;立即只读 conflicted,只显示一次本地导出与刷新", + "expected_db_files": "仅 A 状态写入;B 的 state_version/save_status/latest_exports/project 数量不变", + "forbidden_side_effects": "B 不自动重试覆盖、不 merge、不另存项目、不恢复自动保存", + "evidence_files": "两标签 response.json、db-diff.json、trace.zip、screenshots/conflicted.png" + }, + { + "case_id": "TDD-WP6-PRIV-001-ack", + "parent_family": "TDD-WP6-PRIV-001", + "requirement_ac_source": "ADMIN-05、17.1、AC-25", + "fixture_ids": [ + "FX-ACTOR-ADMIN-A" + ], + "fixture_and_preconditions": "FX-ACTOR-ADMIN-A 未确认 current notice v1", + "numbered_steps": "1. 进入私有区域;2. 得到 428;3. 以 expected v1 调 notice ack;4. 重试区域摘要", + "expected_response_ui": "首次 428 PRIVATE_CONTENT_NOTICE_ACK_REQUIRED 只含 version/message_key;ack 成功后区域仅显示安全摘要", + "expected_db_files": "只更新该 admin 的 notice version/ack time;幂等重试不新增语义变化", + "forbidden_side_effects": "ack 不创建 PrivateContentAccessLog;不返回正文/图片;不写 Cookie/localStorage 作为真相", + "evidence_files": "response.json、db-diff.json、trace.zip、screenshots/notice.png" + }, + { + "case_id": "TDD-WP6-PRIV-001-persistence", + "parent_family": "TDD-WP6-PRIV-001", + "requirement_ac_source": "ADMIN-05、AC-25", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "admin A 已确认 v1", + "numbered_steps": "1. 新标签;2. API 重启;3. 浏览器重启/重登;4. 在真实 Chrome 与 Edge 登录同一 admin", + "expected_response_ui": "同版本均不重复提示,session/bootstrap 计算 notice_acknowledged=true", + "expected_db_files": "UserProfile ack 字段不因浏览器/会话改变;每次 session 独立但 ack 不重复写", + "forbidden_side_effects": "Cookie/session/localStorage/IndexedDB 清空不使服务端遗忘;跨管理员不共享 ack", + "evidence_files": "每环境 response.json、db-diff.json、trace.zip、screenshots/ack-persisted.png" + }, + { + "case_id": "TDD-WP5-PREV-001-grant-lifecycle", + "parent_family": "TDD-WP5-PREV-001", + "requirement_ac_source": "ADMIN-07、17.13、AC-42、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "普通账号,active/revoked/expired grant,已有项目引用预览版本", + "numbered_steps": "1. admin 授权批次;2. 用户读取/添加;3. 撤销/到期;4. 再读取/添加;5. 重开已有项目", + "expected_response_ui": "active 时只见授权项;撤销/到期立即阻断新读取/添加;已有项目按保存版本渲染", + "expected_db_files": "grant 状态/审计更新;UserProfile.role 不变;项目引用版本不改写", + "forbidden_side_effects": "不出现后台权限、不把 preview 写 public manifest/cache、不因撤销破坏既有项目", + "evidence_files": "response.json、db-diff.json、cache-enumeration.json、trace.zip" + }, + { + "case_id": "TDD-WP5-COL-001-four-layouts", + "parent_family": "TDD-WP5-COL-001", + "requirement_ac_source": "COL-01 至 05、AC-15 P0-A 子集、16、32", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "COLOR001/002/008/016 与同一五色 fixture", + "numbered_steps": "1. 渲染四卡;2. 改裁剪/滤镜;3. 换底图;4. 尝试访问其他色卡", + "expected_response_ui": "四布局消费完全相同五色;刷新规则一致;其他色卡不可发现", + "expected_db_files": "只保存稳定 ID/version/五色 snapshot", + "forbidden_side_effects": "不启用其余 12 色卡、不各自重新提色、不联网读取图片", + "evidence_files": "palette.json、pixel-diff.json、manifest.json" + }, + { + "case_id": "TDD-WP2-PROJ-002-save-failure-recovery", + "parent_family": "TDD-WP2-PROJ-002", + "requirement_ac_source": "PROJECT-04、05、EXPORT-06、AC-20、35", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "saved N;页面创建 dirty;参数化网络失败、storage unavailable、强制刷新/崩溃", + "numbered_steps": "1. 触发保存失败;2. 检查 failed/离开弹层;3. 页面保持时恢复并重试;4. 另一路强制退出后重开;5. 枚举浏览器存储", + "expected_response_ui": "失败不显示 saved;正常恢复可保存;强制退出只恢复后端 N 且撤销栈为空", + "expected_db_files": "失败期间 DB 不变;成功重试才 N+1;浏览器持久存储无草稿/提示词/私有引用", + "forbidden_side_effects": "不把失败当成功;不在关闭后恢复内存草稿;不让 SSE 代替 REST 真相", + "evidence_files": "response.json、db-diff.json、cache-enumeration.json、trace.zip、screenshots/save-failed.png" + }, + { + "case_id": "TDD-WP6-PRIV-001-version-bump", + "parent_family": "TDD-WP6-PRIV-001", + "requirement_ac_source": "ADMIN-05、AC-25", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "admin A/B 已确认 v1,应用 current notice 切到 v2", + "numbered_steps": "1. 两个 admin 分别进入区域;2. 尝试用 expected v1 ack;3. 读取 v2 并确认", + "expected_response_ui": "两者重新 428;旧 expected version 失败且要求重读;各自确认 v2 后可进入摘要", + "expected_db_files": "旧 ack 保留历史语义但当前字段仅在正确 v2 ack 后更新;A/B 独立", + "forbidden_side_effects": "不以旧确认放行;不因 A 确认替 B 确认;不返回私有正文", + "evidence_files": "response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP0-DEP-001-frozen-toolchain", + "parent_family": "TDD-WP0-DEP-001", + "requirement_ac_source": "NFR-09、DevelopmentPlan 2.1、13.1", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "冻结 lockfile、干净离线依赖缓存、win-x64 runner", + "numbered_steps": "1. 严格按 lockfile 安装;2. 编译 TypeScript;3. 最小加载 React/Fabric/Fastify/better-sqlite3;4. 用随包 Node 运行 native smoke", + "expected_response_ui": "所有命令退出 0,版本与 lockfile/DevelopmentPlan 一致", + "expected_db_files": "只生成声明的构建缓存与报告;业务 DB/LocalDataRoot 无变化", + "forbidden_side_effects": "不升级/替换依赖、技术栈或 native 模块;不联网漂移解析版本", + "evidence_files": "commands.json、dependency-tree.json、native-smoke.json" + }, + { + "case_id": "TDD-WP0-STO-002-equal-limit", + "parent_family": "TDD-WP0-STO-002", + "requirement_ac_source": "17.19、NFR-05、AC-55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "已计量+活动预留=5,368,709,119,预计写 1 字节,可写目录", + "numbered_steps": "1. 调受管二进制写入预检;2. 提交文件/DB;3. 重读 storage state", + "expected_response_ui": "不得返回 507;写入成功后 notice/status 为 full", + "expected_db_files": "建立并消费一份 reservation;managed bytes 精确为 5,368,709,120;managed file 已提交;last_measured_at 更新", + "forbidden_side_effects": "不用 \u003e= 拒绝;不回滚已成功等号写入;不调用自动清理", + "evidence_files": "response.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP1-AUTH-004-session-revocation", + "parent_family": "TDD-WP1-AUTH-004", + "requirement_ac_source": "AUTH-04 至 07、AC-22、33、49", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "每个参数使用独立库:active 普通用户或 active super_admin 各有 2 个 session", + "numbered_steps": "1. 验证正常 session;2. 分别触发普通用户 logout/暂停/注销或管理员 logout/停用/移出白名单;3. 再调受保护 API", + "expected_response_ui": "撤销前成功;每种撤销后均为 401 或相应身份入口,已打开页面退出旧数据;两种 audience 不能互用", + "expected_db_files": "相应主体全部 session revoked_at 在同一事务写入;账号/白名单状态按触发操作变化", + "forbidden_side_effects": "不只撤销当前浏览器;不把普通 session 提升为后台 session;不保留可交互私有响应", + "evidence_files": "每参数 response.json、db-diff.json、trace.zip、screenshots/revoked.png" + }, + { + "case_id": "TDD-WP0-API-001-schema-envelope", + "parent_family": "TDD-WP0-API-001", + "requirement_ac_source": "NFR-05、DevelopmentPlan 6.1、6.6", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "生成 TypeBox/OpenAPI/客户端 snapshot;合法与非法 fixture", + "numbered_steps": "1. 生成 OpenAPI 3.1;2. 比较 snapshot;3. 对每个 operation 注入合法/非法请求;4. 校验错误信封和 correlation ID", + "expected_response_ui": "Schema/运行/客户端一致;非法响应使用冻结 HTTP/code/message_key/details 白名单", + "expected_db_files": "只读场景 DB 无变化;写场景失败按各 operation 事务回滚", + "forbidden_side_effects": "不出现未声明字段/枚举、供应商原文、堆栈、路径或缺失 correlation ID", + "evidence_files": "openapi.json、snapshot-diff.json、response.json、redaction.json" + }, + { + "case_id": "TDD-WP2-ERR-001-unknown-retryable", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-09、16、17.18、AC-04、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "独立 unknown_retryable fixture;已提交任务", + "numbered_steps": "1. 触发未知临时错误;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询任务和脱敏诊断", + "expected_response_ui": "原任务 failed/category=unknown_retryable;只提供“稍后重试”;动作后同项目新增一个 GenerationJob", + "expected_db_files": "原任务 release 一次、无 output;新任务独立 reserve;只保留非敏感诊断", + "forbidden_side_effects": "不显示“使用原输入重试”、修改输入、选模型或联系管理员;不泄漏原始错误或自动重试", + "evidence_files": "response.json、worker-events.json、db-diff.json、screenshots/unknown-retryable.png" + }, + { + "case_id": "TDD-WP0-BND-001-loopback-origin", + "parent_family": "TDD-WP0-BND-001", + "requirement_ac_source": "NFR-09、AC-24", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "API 未启动;回环、局域网、错误 Host/Origin 和预检请求参数", + "numbered_steps": "1. 启动产品包;2. 检查监听地址;3. 从允许/拒绝地址请求;4. 发送跨源/CORS 预检", + "expected_response_ui": "只接受 127.0.0.1:43121 与同源;其他路径连接失败或按门禁拒绝", + "expected_db_files": "无业务表、会话、外部用量变化", + "forbidden_side_effects": "不监听 0.0.0.0/IPv6 wildcard,不建防火墙入站规则,不返回 CORS 放行头", + "evidence_files": "socket-listeners.json、response.json、firewall-diff.json" + }, + { + "case_id": "TDD-WP2-GEN-003-stale-config", + "parent_family": "TDD-WP2-GEN-003", + "requirement_ac_source": "GEN-15、ADMIN-03、AC-30", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "用户已确认旧 model_config_version/cost;管理员原子发布新集合", + "numbered_steps": "1. 旧页面直接提交;2. 读取 412 摘要;3. 重新 GET models 并明确确认;4. 再提交", + "expected_response_ui": "第一次 412 且要求重新确认;第二次才可创建任务", + "expected_db_files": "第一次 job/project/reference/reserve/outbox 全无变化;第二次按新快照创建", + "forbidden_side_effects": "第一次不上游、不持久化参考图、不冻结;不自动替用户切推荐模型", + "evidence_files": "response.json、db-diff.json、external-calls.json、trace.zip" + }, + { + "case_id": "TDD-WP2-ERR-001-unknown-non-retryable", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-09、16、17.18、AC-04、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "独立 unknown_non_retryable fixture;已提交任务", + "numbered_steps": "1. 触发未知不可重试错误;2. 检查唯一下一步为“联系管理员”;3. 尝试寻找或调用重复提交动作;4. 查询任务", + "expected_response_ui": "原任务 failed/category=unknown_non_retryable;只提供“联系管理员”,无重试或修改入口", + "expected_db_files": "release 一次;无 output;原草稿保留但本 case 不创建新 job", + "forbidden_side_effects": "不自动重试、不循环轮询制造新任务、不降级为 unknown_retryable、不提供其他类别动作", + "evidence_files": "response.json、worker-events.json、db-diff.json、screenshots/unknown-non-retryable.png" + }, + { + "case_id": "TDD-WP2-PROJ-004-conflict-export-once", + "parent_family": "TDD-WP2-PROJ-004", + "requirement_ac_source": "PROJECT-07、EXPORT-05、AC-36 P0-A 子集", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "conflicted 标签页,conflict_export_used=false", + "numbered_steps": "1. 本地导出一次(成功和失败各参数);2. 再次尝试;3. 刷新最新版本", + "expected_response_ui": "第一次结束后立即禁用再次导出;结果明确“仅下载本页版本,未写入项目”;刷新到后端最新状态", + "expected_db_files": "所有步骤不调用 latest API,projects/project_states/latest_exports 无变化;flag 只在页面内存", + "forbidden_side_effects": "不因第一次失败允许第二次;不覆盖后端、不另存副本、不把 flag 持久化", + "evidence_files": "network-timeline.json、db-diff.json、trace.zip、screenshots/conflict-export.png" + }, + { + "case_id": "TDD-WP1-SLOT-001-stage-limit", + "parent_family": "TDD-WP1-SLOT-001", + "requirement_ac_source": "AUTH-01、NFR-04、AC-32、45", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化 9/10 个 active+suspended 普通账号、deleted、多个 super_admin", + "numbered_steps": "1. 发码前尝试第 10/11 人;2. 在发码后并发占满名额再完成;3. 删除普通账号再注册", + "expected_response_ui": "第 10 人可成功,第 11 人发码/完成均阻断;deleted 释放;super_admin 不占名额", + "expected_db_files": "成功时计数/账号/邀请原子;失败零变化;累计统计保留 deleted", + "forbidden_side_effects": "不把 suspended/deleted/super_admin 计错,不通过并发超 10,不让失败占邀请码", + "evidence_files": "response.json、db-diff.json、concurrency-trace.json" + }, + { + "case_id": "TDD-WP6-SVC-001-hard-quota", + "parent_family": "TDD-WP6-SVC-001", + "requirement_ac_source": "ADMIN-05、08、NFR-08、17.15、AC-47", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "Resend 日 79/80、月 2399/2400;高德 999/1000;provider 异常;已有 session", + "numbered_steps": "1. 并发领取最后额度;2. 尝试下一次;3. 注入 provider 故障;4. 检查登录/注册/DYN004/现有 session", + "expected_response_ui": "最后一份已领取调用可结束,后续不调用;状态 paused_quota/provider;已有 session 继续;手动地点继续", + "expected_db_files": "external_service_usage 事务计数/status/reason 更新;失败不创建验证码假成功", + "forbidden_side_effects": "不发送第 81/超月/第 1001 次,不影响已有 session,不切付费/备用,不建运行时邮件质量状态", + "evidence_files": "response.json、db-diff.json、external-calls.json、concurrency-trace.json" + }, + { + "case_id": "TDD-WP2-GEN-002-success", + "parent_family": "TDD-WP2-GEN-002", + "requirement_ac_source": "GEN-06、08、11、CREDIT-03、AC-03", + "fixture_ids": [ + "FX-GATEWAY-SYNC" + ], + "fixture_and_preconditions": "上一 case 的 queued job + FX-GATEWAY-SYNC", + "numbered_steps": "1. Worker 领取;2. 调上游并验证单图;3. 落盘;4. 查询任务/项目", + "expected_response_ui": "queued→running→succeeded;项目可进入编辑且历史新增一图", + "expected_db_files": "lease/heartbeat、output managed file、output_asset、generation_commit、project history/draft_state 一次提交;容量增加实测字节", + "forbidden_side_effects": "不在文件安全落盘前 succeeded;不保存多图;不重复扣点;不改变项目比例", + "evidence_files": "worker-events.json、db-diff.json、fs-after.json、screenshots/succeeded.png" + }, + { + "case_id": "TDD-WP2-EXP-001-result-matrix", + "parent_family": "TDD-WP2-EXP-001", + "requirement_ac_source": "EXPORT-05、06、AC-35、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化:合成失败、下载失败、project 保存失败、latest 持久化失败、storage full/unavailable", + "numbered_steps": "1. 在 UIDesign 四结果组合及存储状态下导出;2. 记录下载/上传调用顺序;3. 重读项目/latest", + "expected_response_ui": "分别显示冻结结果文案;可合成时允许规定本地下载;下载失败可复用同一次合成;后端失败明确 latest 未更新", + "expected_db_files": "失败分支保留旧 latest;project 保存失败不写新 state;latest 失败不写 latest/managed file;成功分支才更新", + "forbidden_side_effects": "不撤回已下载文件;不以本地下载覆盖后端记录;不显示双成功假状态;不把 Blob 存浏览器持久缓存", + "evidence_files": "每参数 network-timeline.json、response.json、db-diff.json、trace.zip、screenshots/result.png" + }, + { + "case_id": "TDD-WP0-STO-003-unavailable-matrix", + "parent_family": "TDD-WP0-STO-003", + "requirement_ac_source": "17.19、NFR-05、AC-35、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "LocalDataRoot 不可写,再参数化 SQLite 仍可写/不可写", + "numbered_steps": "1. 尝试 project JSON 保存、生成和二进制写;2. 读取/下载/仅下载;3. 尝试删除/清理", + "expected_response_ui": "新增后端写全部失败且 UI 为 failed/unavailable;读取、下载和可行的仅下载保留;SQLite 不可写时删除/排队明确未提交", + "expected_db_files": "不更新 state_version/managed bytes/latest/job/ledger;SQLite 可写时允许规定逻辑删除/队列,反之无 DB 变化", + "forbidden_side_effects": "不显示已保存;不上游;不把内存草稿写浏览器持久存储;不伪造清理完成", + "evidence_files": "response.json、db-diff.json、trace.zip、screenshots/unavailable.png" + }, + { + "case_id": "TDD-WP7-EXT-001-three-real-models", + "parent_family": "TDD-WP7-EXT-001", + "requirement_ac_source": "GEN-13、AC-40、41", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "三模型各自真实凭据/配置,显式执行确认", + "numbered_steps": "1. 显示模型/次数/额度;2. 分别执行纯文字、参考图、四比例、sync/async/查询/响应/错误;3. 脱敏;4. 绑定 config_version", + "expected_response_ui": "三模型独立完整矩阵通过才解除;任一缺项保持 externally_blocked", + "expected_db_files": "只写对应模型验证证据/runtime 状态和允许审计;测试任务/点数按隔离规则", + "forbidden_side_effects": "不共享证据、不自动充值、不保留真实输入/图片/原始响应、不在普通 CI 调用", + "evidence_files": "每模型 contract-matrix.json、external-calls.json、redaction.json、manual-review.json" + }, + { + "case_id": "TDD-WP7-SEC-001-artifact-leak-scan", + "parent_family": "TDD-WP7-SEC-001", + "requirement_ac_source": "PRIV-01、02、NFR-09、AC-41、48、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "ZIP、源码、Git、DB 配置、manifest、日志、截图、trace/evidence 全集合;秘密 trap", + "numbered_steps": "1. 解包并扫描文本/二进制/元数据;2. 检查 Windows 用户名/绝对路径;3. 检查缓存/凭据位置;4. 人工复核命中", + "expected_response_ui": "真实敏感值和硬编码用户路径零命中;凭据仅 Credential Manager;诊断通过同一扫描", + "expected_db_files": "只生成扫描报告;不修改被扫描制品", + "forbidden_side_effects": "不把 allowlist/HMAC/验证码/session/提示词/图片/原始错误写证据,不用删除报告掩盖命中", + "evidence_files": "scan-report.json、allowlist.json、manual-review.json" + }, + { + "case_id": "TDD-WP1-CFG-001-secure-revision", + "parent_family": "TDD-WP1-CFG-001", + "requirement_ac_source": "AUTH-07、ADMIN-09、AC-49、50", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "current applied revision N;参数化完整 N+1、HMAC/凭据缺失、身份冲突", + "numbered_steps": "1. 离线写候选安全配置;2. API 启动应用;3. 查询 apply state/session;4. 注入失败重试", + "expected_response_ui": "完整配置原子生效并推进 revision;失败保持 N 并阻止受影响入口", + "expected_db_files": "成功同事务更新 apply state、撤销受影响 session、写 system/backend_secure_config 审计;失败全回滚", + "forbidden_side_effects": "不写白名单/HMAC/pepper/人员身份/凭据,不出现半生效配置", + "evidence_files": "config-result.json、db-diff.json、redaction.json" + }, + { + "case_id": "TDD-WP0-BRW-001-real-supported", + "parent_family": "TDD-WP0-BRW-001", + "requirement_ac_source": "NFR-01、AC-24、41", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "最终 RELEASE.json;真实 Chrome/Edge 独立 profile", + "numbered_steps": "1. 分别启动记录的 full version;2. 完成 UA-CH support check;3. 进入 bootstrap/login;4. 复查产品 API", + "expected_response_ui": "两浏览器均通过并建立短期支持 cookie,产品 API 每次复核品牌/major", + "expected_db_files": "仅门禁所需非敏感记录/cookie;不建用户 session", + "forbidden_side_effects": "不用 Playwright Chromium、伪 UA 或前一 major 代替;不联网查询当前版本", + "evidence_files": "每浏览器 environment.json、response.json、trace.zip、screenshots/supported.png" + }, + { + "case_id": "TDD-WP3-ADP-001-per-model-contract", + "parent_family": "TDD-WP3-ADP-001", + "requirement_ac_source": "GEN-01、02、05、06、13、16、AC-40", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "三个模型各自独立 mock/真实受控账号和脱敏 fixture", + "numbered_steps": "1. 每模型分别执行纯文字、合法参考图、四比例;2. 覆盖同步/异步与查询;3. 验证 MIME/尺寸/单图;4. 注入九类映射", + "expected_response_ui": "每模型只按自身已证实协议成功;结果恰一图;错误映射到固定 category", + "expected_db_files": "真实验证只写对应 model/config_version 的 evidence 状态;任务/点数按独立用例结算", + "forbidden_side_effects": "不从一个模型推断另两个、不共享 verified 标记、不接受多图/未知字段、不记录原始响应", + "evidence_files": "每模型 contract-matrix.json、response.json、external-calls.json、redaction.json" + }, + { + "case_id": "TDD-WP6-ADM-001-role-and-summary", + "parent_family": "TDD-WP6-ADM-001", + "requirement_ac_source": "ADMIN-01、02、04、08、AC-25、49", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "active/suspended super_admin、普通/预览账号,后台安全摘要 fixture", + "numbered_steps": "1. 各主体请求后台页/API;2. 停用 admin 后复用旧页面;3. 浏览列表/详情;4. 检查私有默认", + "expected_response_ui": "仅 active super_admin 可进入;停用立即退出;默认只见脱敏元数据,普通/预览无后台入口", + "expected_db_files": "读取无业务变化;拒绝分支无审计/私有读取;规定登录审计独立存在", + "forbidden_side_effects": "不共享普通/后台 session,不因预览 grant 获后台,不默认返回提示词/图片/密钥", + "evidence_files": "每主体 response.json、db-access.json、trace.zip、screenshots/admin-denied.png" + }, + { + "case_id": "TDD-WP1-ADM-001-admin-auth-boundary", + "parent_family": "TDD-WP1-ADM-001", + "requirement_ac_source": "AUTH-07、AC-49", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "白名单/非白名单邮箱、已存在普通账号冲突、两个 admin、全部停用状态", + "numbered_steps": "1. 后台发码;2. 首次验证/后续登录;3. 尝试普通 session 访问后台;4. 停用全部并尝试自助恢复", + "expected_response_ui": "非白名单发码前拒绝;白名单首次建 super_admin;audience 严格分离;无最后管理员保护且无自助恢复", + "expected_db_files": "允许分支建 admin/session/审计;拒绝分支不建 challenge/session;停用撤销 session", + "forbidden_side_effects": "不泄漏白名单、不使用邀请码/普通名额、不原地提升普通账号、不创建部署人员角色", + "evidence_files": "每参数 response.json、db-diff.json、external-calls.json、trace.zip" + }, + { + "case_id": "TDD-WP4-CAN-001-fifty-elements", + "parent_family": "TDD-WP4-CAN-001", + "requirement_ac_source": "EDITOR-04 至 09、17.5、AC-27", + "fixture_ids": [ + "FX-CANVAS-50" + ], + "fixture_and_preconditions": "FX-CANVAS-50,稳定选择重叠 fixture", + "numbered_steps": "1. 添加到 50;2. 循环选择/长按候选/框选/多选移动;3. 层级/复制/删除;4. 保存/导出;5. 添加第 51", + "expected_response_ui": "前 50 可编辑保存导出;选择顺序确定;第 51 明确阻断且布局不跳", + "expected_db_files": "Canvas 最多 50,成功操作各一次撤销/保存;第 51 无 state change", + "forbidden_side_effects": "不出现图层面板/隐藏/锁定/编组,不因阻断损坏已有元素", + "evidence_files": "canvas-state.json、db-diff.json、trace.zip、performance.json" + }, + { + "case_id": "TDD-WP3-MDL-003-runtime-recommendation", + "parent_family": "TDD-WP3-MDL-003", + "requirement_ac_source": "GEN-15、ADMIN-03、17.2、AC-30、51", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "三模型 verified/available,配置快照和哈希已记录", + "numbered_steps": "1. 依次模拟默认模型不可用、全部不可用、一个原因恢复但另一原因仍在、全部恢复;2. 每步 GET models", + "expected_response_ui": "configured default 始终原 ID;recommended 按可用模型 priority 派生或 null;reason 使用固定优先级", + "expected_db_files": "只更新 model_runtime_availability/GatewayBalanceState/runtime version;config set/version/hash 不变", + "forbidden_side_effects": "不写 enabled/is_default/priority;不自动恢复;不把推荐变成第二默认或用户隐式选择", + "evidence_files": "每步 response.json、db-diff.json、screenshots/model-states.png" + }, + { + "case_id": "TDD-WP4-STK-001-transform-sticker", + "parent_family": "TDD-WP4-STK-001", + "requirement_ac_source": "EDITOR-04 至 09、PROJECT-04、AC-13、27", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "普通贴纸元素和重叠选择 fixture", + "numbered_steps": "1. 移动/缩放/旋转/翻转/透明度;2. 复制/层级/多选;3. 撤销重做;4. 保存/导出重开", + "expected_response_ui": "画布、属性控件、导出和重开状态一致;每次连续手势是一项撤销", + "expected_db_files": "Canvas element snapshot/state_version 按提交更新", + "forbidden_side_effects": "不改变 stable ID/resource_version,不引入隐藏/锁定/编组,不丢选择", + "evidence_files": "canvas-state.json、db-diff.json、pixel-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP3-MDL-002-contract-change", + "parent_family": "TDD-WP3-MDL-002", + "requirement_ac_source": "GEN-13、15、ADMIN-03、AC-30、40", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "verified 模型集合", + "numbered_steps": "1. 修改端点/字段/参考图/响应/异步查询/error mapping 任一契约字段;2. 保存;3. GET models;4. 尝试生成", + "expected_response_ui": "保存成功但对应模型 contract unverified、runtime reason contract_unverified;生成被阻断直到独立重验", + "expected_db_files": "新 config_version/set;对应 runtime 独立置 false;审计/outbox 同事务", + "forbidden_side_effects": "不沿用旧 verified 证据;不暗改 enabled/default/priority;不调用真实上游", + "evidence_files": "每参数 response.json、db-diff.json、external-calls.json" + }, + { + "case_id": "TDD-WP0-BRW-002-hard-block", + "parent_family": "TDD-WP0-BRW-002", + "requirement_ac_source": "NFR-01、AC-24", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化其他品牌、错误 major、非 Windows、UA-CH 缺失/冲突/解析失败和原始 API 客户端", + "numbered_steps": "1. 请求 support check;2. 请求 HTML/bundle/API;3. 尝试登录/项目/后台深链;4. 查 DB/外部调用", + "expected_response_ui": "全部 426 BROWSER_UNSUPPORTED,reason 仅四类,页面只有重新打开说明且无绕过", + "expected_db_files": "业务 DB 读取/写入计数为 0,session/验证码/资源读取均无变化", + "forbidden_side_effects": "不返回继续令牌/参数,不发 Resend/AI/高德,不泄露产品数据", + "evidence_files": "每参数 response.json、db-access.json、external-calls.json、screenshots/blocked.png" + }, + { + "case_id": "TDD-WP5-MAN-001-readonly-compiler", + "parent_family": "TDD-WP5-MAN-001", + "requirement_ac_source": "TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、STATIC-01 至 STATIC-04、AC-32、42", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "只读规范入口与独立输出目录;Lua/Prefab/证据目录 trap", + "numbered_steps": "1. 记录源 hash/mtime;2. 运行声明式转换;3. 注入尝试执行 Lua/Prefab/读取证据目录;4. 复核输出", + "expected_response_ui": "只消费允许 catalog/metadata/资源;非法入口失败;输出 manifest 可重现", + "expected_db_files": "只写版本化派生输出;源 hash/mtime 零变化", + "forbidden_side_effects": "不执行 Android runtime、不扫描证据目录、不复制约 3.57 GB 全库、不覆盖源", + "evidence_files": "compiler-report.json、source-before.json、source-after.json、output-manifest.json" + }, + { + "case_id": "TDD-WP1-AUD-001-immutable-triggers", + "parent_family": "TDD-WP1-AUD-001", + "requirement_ac_source": "ADMIN-05、09、17.10、17.16、AC-50", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "各一条 AdminOperationLog/PrivateContentAccessLog;普通、retention、privacy purge 连接", + "numbered_steps": "1. 普通连接 UPDATE/DELETE;2. 伪造 purge 函数;3. 合法作用域在到期/注销执行", + "expected_response_ui": "普通和伪造操作被 trigger 阻断;仅注册作用域执行规定清理", + "expected_db_files": "非法尝试零变化;合法作用域只删除/匿名化允许行并留非敏感结果", + "forbidden_side_effects": "不允许后台/API 任意修改,不混用两类日志,不把正文写审计", + "evidence_files": "sql-results.json、db-diff.json、trigger-definition.json" + }, + { + "case_id": "TDD-WP6-STATE-001-three-model-states", + "parent_family": "TDD-WP6-STATE-001", + "requirement_ac_source": "GEN-15、ADMIN-03、08、AC-30、51", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "default 不可用、其他推荐、全不可用、恢复参数", + "numbered_steps": "1. 打开总览/模型页;2. 注入 runtime 变化;3. SSE 后 REST refetch;4. 恢复一个原因", + "expected_response_ui": "configured default、逐模型 runtime、recommended 三列独立;推荐可 null;UI 不暗换用户选择", + "expected_db_files": "只改 runtime/version;ModelConfig/current set 无变化", + "forbidden_side_effects": "不合并三种状态、不把 SSE 当真相、不回写默认/enabled/priority", + "evidence_files": "response.json、db-diff.json、screenshots/model-columns.png、trace.zip" + }, + { + "case_id": "TDD-WP4-DYN-002-location-consent", + "parent_family": "TDD-WP4-DYN-002", + "requirement_ac_source": "DYN-04、PRIV-03、AC-17、47", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "DYN004 固定坐标/高德 mock;同意、拒绝、服务暂停参数", + "numbered_steps": "1. 插入前检查 Dada 提示;2. 拒绝;3. 同意并定位/逆地理;4. 手动地点回退;5. 删除元素/注销", + "expected_response_ui": "拒绝调用 0 且不插入坐标;同意后正确显示;暂停时自动定位禁用但手动地点可用", + "expected_db_files": "只在同意后把坐标/文本存实例;删除/注销按规则清理", + "forbidden_side_effects": "前端不持高德密钥,不把坐标用于其他元素/用户,不在提示前调用", + "evidence_files": "external-calls.json、canvas-state.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP2-GEN-001-submit-snapshot", + "parent_family": "TDD-WP2-GEN-001", + "requirement_ac_source": "GEN-01 至 08、PROJECT-01、AC-03、34、44", + "fixture_ids": [ + "FX-CREDIT-ENOUGH", + "FX-MODEL-VERIFIED", + "FX-REF-VALID" + ], + "fixture_and_preconditions": "FX-MODEL-VERIFIED、FX-CREDIT-ENOUGH、FX-REF-VALID,new_project", + "numbered_steps": "1. multipart 提交 prompt/model/ratio/config version/cost/reference;2. 查询任务;3. 检查 staging/正式文件", + "expected_response_ui": "成功响应为 queued,返回同一任务真相;UI 不显示假百分比", + "expected_db_files": "新 project、job、reference snapshot、reserve ledger、outbox 同事务;提交后 reference 从 staging 进入私有 managed file", + "forbidden_side_effects": "不接受未冻结高级字段;不让 Worker 领取未完整提交任务;不暴露路径;失败补偿不留下可读半文件", + "evidence_files": "request.json、response.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP1-AUTH-004-challenge-guards", + "parent_family": "TDD-WP1-AUTH-004", + "requirement_ac_source": "AUTH-02、04、AC-33", + "fixture_ids": [ + "FX-CHALLENGE" + ], + "fixture_and_preconditions": "FX-CHALLENGE、可控时钟、active 普通账号", + "numbered_steps": "1. 成功消费验证码;2. 重放同码;3. 新建 challenge 后前进超过 10 分钟再提交;4. 发码后 60 秒内再发;5. 连续错误触发限流", + "expected_response_ui": "仅首次有效提交成功;重放/过期/错误使用稳定错误;60 秒重发和高频为 429;页面不显示假成功", + "expected_db_files": "首次 challenge consumed_at 只写一次;失败次数/限流记录按规则变化;失败分支不新增 session", + "forbidden_side_effects": "不记录验证码明文;不延长原 challenge;限流失败不调用 Resend、不创建会话", + "evidence_files": "每参数 response.json、db-diff.json、external-calls.json" + }, + { + "case_id": "TDD-WP2-GEN-001-concurrent-reserve", + "parent_family": "TDD-WP2-GEN-001", + "requirement_ac_source": "CREDIT-03、GEN-07、AC-05、29", + "fixture_ids": [ + "FX-CREDIT-ENOUGH" + ], + "fixture_and_preconditions": "FX-CREDIT-ENOUGH,两个不同 submission/idempotency key 同时开始", + "numbered_steps": "1. 用 barrier 并发提交两次;2. 等待响应;3. 查询 current job、余额和流水", + "expected_response_ui": "一个响应创建任务,另一个 200 返回同一现有任务引用;UI 提供返回当前任务", + "expected_db_files": "仅一个 queued/running job、一个 project(new_project 模式)、一条 reserve ledger;余额只转移一次", + "forbidden_side_effects": "不创建第二项目/参考关系/outbox/upstream call;available 不为负", + "evidence_files": "两份 response.json、db-diff.json、concurrency-trace.json" + }, + { + "case_id": "TDD-WP3-MDL-002-invalid-set", + "parent_family": "TDD-WP3-MDL-002", + "requirement_ac_source": "ADMIN-03、17.2、AC-30", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化缺替代、非法替代、priority 缺失/浮点/0/负数/重复、缺模型/未知模型", + "numbered_steps": "1. 分别 PUT 完整候选;2. 读取稳定错误;3. GET 当前集合", + "expected_response_ui": "返回对应 400/409 稳定 code;UI 聚焦修正字段;当前集合仍为 N", + "expected_db_files": "current pointer、versions/runtime/审计成功记录/outbox 均无变化", + "forbidden_side_effects": "不生成孤儿候选 set;不部分改变 enabled/default/priority;不新增错误分类", + "evidence_files": "每参数 response.json、db-diff.json" + }, + { + "case_id": "TDD-WP2-EXP-001-same-blob-success", + "parent_family": "TDD-WP2-EXP-001", + "requirement_ac_source": "EXPORT-04、AC-23、35", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "saved project,已有旧 JPG latest;新客户端 Blob B", + "numbered_steps": "1. 记录 B SHA-256;2. 用 B 触发浏览器下载;3. 同一 B 调 EXPORT_SAVE;4. 重读 latest 并下载", + "expected_response_ui": "UI 显示“已下载并保存为最新成品”;两个通道 hash 相同", + "expected_db_files": "原子替换该格式 latest_exports/managed file;旧 JPG 进入规定清理;PNG latest 不变;容量按真实文件更新", + "forbidden_side_effects": "不重新编码第二份 Blob;不保留完整导出历史;不影响另一格式;幂等重试不重复计量", + "evidence_files": "download.json、request.json、response.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP7-EXT-002-real-resend", + "parent_family": "TDD-WP7-EXT-002", + "requirement_ac_source": "AUTH-01、02、07、AC-01、33、41、47、49", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "已验证发信域名、正式受控普通/admin 邮箱、免费规则记录", + "numbered_steps": "1. 核 SPF/DKIM/免费规则;2. QQ/163/至少一种企业邮箱各发 20 封;3. 统计 2 分钟送达;4. 真实完成普通/admin 认证", + "expected_response_ui": "每类至少 19/20 在 2 分钟内且两类认证成功;否则阻止发布但不改 runtime service_status", + "expected_db_files": "只保存人工汇总/证据 hash;不建逐封 EmailVerificationEvent", + "forbidden_side_effects": "不用预置账号/mock 冒充,不保存验证码/真实邮箱进证据,不切付费备用", + "evidence_files": "delivery-summary.json、domain-check.json、auth-result.json、redaction.json" + }, + { + "case_id": "TDD-WP2-ERR-001-upstream-timeout", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-09、16、17.18、AC-04、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "独立 upstream_timeout fixture;已提交任务超时", + "numbered_steps": "1. 触发超时;2. 检查唯一下一步为“使用原输入重试”;3. 点击该动作;4. 查询原任务、新任务和项目", + "expected_response_ui": "原任务 failed/category=upstream_timeout;只提供“使用原输入重试”;动作后同项目新增一个 GenerationJob", + "expected_db_files": "原任务 release 一次且无 output;新任务独立 reserve 并保存相同输入关系快照", + "forbidden_side_effects": "不改成“稍后重试”;不要求修改输入/选择模型/联系管理员;不复用原任务 ID 或创建第二项目", + "evidence_files": "response.json、worker-events.json、db-diff.json、screenshots/upstream-timeout.png" + }, + { + "case_id": "TDD-WP2-PROJ-005-failed-draft-retry", + "parent_family": "TDD-WP2-PROJ-005", + "requirement_ac_source": "PROJECT-01、GEN-09、AC-04、43", + "fixture_ids": [ + "FX-PROJECT-EMPTY" + ], + "fixture_and_preconditions": "FX-PROJECT-EMPTY 和含成功图项目", + "numbered_steps": "1. 失败页重试两次;2. 返回工作台主动新建;3. 多选批量删除 failed_empty 与成功项目", + "expected_response_ui": "重试只在原 project 新增 job;主动新建才新 project;批量只接受无成功图草稿", + "expected_db_files": "原草稿保留 prompt/config;job 数递增;批量合格项 trashed,成功项目不变", + "forbidden_side_effects": "不把重试变新项目、不把成功项目纳入快捷批量、不丢草稿输入", + "evidence_files": "response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP7-EXT-003-real-amap", + "parent_family": "TDD-WP7-EXT-003", + "requirement_ac_source": "DYN-04、PRIV-03、AC-17、41、47", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "个人认证高德 Web 服务 Key、安全绑定和显式确认", + "numbered_steps": "1. 核接口/QPS/白名单/安全/免费规则;2. 执行定位/逆地理;3. 验证 1,000 硬限模拟/受控测试;4. 脱敏坐标证据", + "expected_response_ui": "接口、绑定和应用硬停止通过才解除;失败保持 externally_blocked", + "expected_db_files": "只写非敏感用量/状态/证据;测试坐标不关联真实项目", + "forbidden_side_effects": "不暴露 Key/真实用户坐标,不发第 1001 次,不启用付费或自动扩容", + "evidence_files": "amap-contract.json、external-calls.json、redaction.json、manual-review.json" + }, + { + "case_id": "TDD-WP4-EXP-001-cancel-pending-edit", + "parent_family": "TDD-WP4-EXP-001", + "requirement_ac_source": "EXPORT-03、AC-23", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "与上一 case 相同", + "numbered_steps": "1. 打开弹层;2. 取消未提交修改确认;3. 检查画布/撤销/网络", + "expected_response_ui": "弹层关闭或停留在未导出状态;无下载、无成功提示", + "expected_db_files": "state_version、latest_exports、撤销栈、保存队列均不因取消改变", + "forbidden_side_effects": "不提交当前编辑、不合成、不下载、不调 EXPORT_SAVE", + "evidence_files": "network-timeline.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP0-DATA-001-root-validation", + "parent_family": "TDD-WP0-DATA-001", + "requirement_ac_source": "NFR-09、17.19、AC-41、55、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化 repo/project/download/只读素材根、合规外部目录、已初始化后缺失目录", + "numbered_steps": "1. 用离线配置设置各路径;2. 启动;3. 删除已初始化根后重启;4. 调资源 API", + "expected_response_ui": "违规路径拒绝;合规路径可写;缺失根进入 data_missing 且不自动重建;浏览器只见逻辑资源 ID", + "expected_db_files": "仅合规首次初始化创建固定目录/DB;失败不创建散落目录", + "forbidden_side_effects": "不硬编码用户名、不回显绝对路径、不在源码/下载/素材根写业务数据", + "evidence_files": "config-result.json、fs-before.json、fs-after.json、response.json" + }, + { + "case_id": "TDD-WP3-WRK-001-recovery", + "parent_family": "TDD-WP3-WRK-001", + "requirement_ac_source": "GEN-08、09、CREDIT-03、AC-04、51", + "fixture_ids": [ + "FX-GATEWAY-ASYNC", + "FX-GATEWAY-UNKNOWN" + ], + "fixture_and_preconditions": "参数化 FX-GATEWAY-ASYNC 有引用和 FX-GATEWAY-UNKNOWN 无安全引用;在 submit 后终止 Worker", + "numbered_steps": "1. 使租约过期;2. 重启 Worker;3. 有引用分支 poll;4. 无引用分支终结;5. 再重启复查", + "expected_response_ui": "有引用不重新 submit,只 poll 到真实终态;无引用为 failed/unknown_retryable,后台显示成本核对待人工", + "expected_db_files": "租约重新领取;无引用 upstream_outcome_known=false、upstream_cost_reconciliation=pending_manual_review,release 一次", + "forbidden_side_effects": "不把 pending_manual_review 写入 job status;不补扣用户点数;不重复上游 submit/结算", + "evidence_files": "worker-events.json、db-diff.json、external-calls.json" + }, + { + "case_id": "TDD-WP0-SEC-001-credential-channel", + "parent_family": "TDD-WP0-SEC-001", + "requirement_ac_source": "PRIV-01、AC-41", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "Windows Credential Manager 测试条目;API/Worker 各需不同凭据;缺失/清除参数", + "numbered_steps": "1. Supervisor 读取;2. 通过对应匿名管道启动子进程;3. 检查命令行/环境/句柄;4. 清除并重启", + "expected_response_ui": "仅目标子进程能在内存读取一次;缺失时服务明确不可用/启动失败", + "expected_db_files": "不写业务 DB;只写非敏感状态与日志", + "forbidden_side_effects": "凭据不进参数、环境、文件、日志、崩溃诊断;API 不获网关密钥,Worker 不获 Resend/高德密钥", + "evidence_files": "process-env-scan.json、pipe-acl.json、redaction.json" + }, + { + "case_id": "TDD-WP5-CAT-001-catalog-1407", + "parent_family": "TDD-WP5-CAT-001", + "requirement_ac_source": "STATIC-01 至 04、AC-13、32", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "规范 catalog/metadata,含 3 组重复图和 part1-25", + "numbered_steps": "1. 编译/加载目录;2. 校验 ID/part/顺序/计数;3. 滚动全目录;4. 观察原图请求/DOM", + "expected_response_ui": "恰 1,407 项,重复图片保留不同稳定 ID;虚拟列表稳定,原图按需加载", + "expected_db_files": "只读 catalog;不复制或改写源;浏览器仅缓存允许公开项", + "forbidden_side_effects": "不去重业务 ID、不一次下载全量、不暴露绝对路径、不线性增长 DOM", + "evidence_files": "catalog-validation.json、network-timeline.json、dom-count.json、source-hashes.json" + }, + { + "case_id": "TDD-WP4-TXT-001-multiline-template-switch", + "parent_family": "TDD-WP4-TXT-001", + "requirement_ac_source": "TEXT-01 至 06、PROJECT-04、AC-09、10", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "多行文字模板实例、固定变换", + "numbered_steps": "1. 输入含换行文字并完成;2. 切换另一模板;3. 撤销/重做;4. 重开/导出", + "expected_response_ui": "换行持久;空内容按规则拒绝;切模板重置默认样式但保留位置/旋转/整体尺寸", + "expected_db_files": "每次完成一条撤销并保存最新 element snapshot", + "forbidden_side_effects": "不把切模板重置变换、不裁切内容、不保存取消编辑", + "evidence_files": "canvas-state.json、db-diff.json、pixel-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP6-SVC-002-limit-recovery", + "parent_family": "TDD-WP6-SVC-002", + "requirement_ac_source": "ADMIN-08、09、NFR-08、AC-47、52", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "paused 服务;参数化下调 hard_limit、提高/付费/自动扩容、新周期/供应商恢复", + "numbered_steps": "1. 尝试各配置;2. 无确认恢复;3. 建立非敏感恢复检查;4. admin 明确确认", + "expected_response_ui": "只允许不扩大能力的下调并审计;提高/付费/自动扩容拒绝;满足条件+确认后恢复", + "expected_db_files": "成功下调/恢复与 AdminOperationLog 同事务;非法请求零变化", + "forbidden_side_effects": "不自动恢复、不保存凭据/供应商原文、不把暂停伪装成功或提高上限", + "evidence_files": "每参数 response.json、db-diff.json、screenshots/service-state.png" + }, + { + "case_id": "TDD-WP0-EVT-001-rest-refetch", + "parent_family": "TDD-WP0-EVT-001", + "requirement_ac_source": "PROJECT-04、GEN-14、NFR-05、DevelopmentPlan 6.2、AC-20、30", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "可控 SSE 序列:正常、断线、event_id 缺号、runtime/config 变化", + "numbered_steps": "1. 订阅 /api/v1/events;2. 注入事件;3. 断线/缺号;4. 观察 REST 请求和 UI", + "expected_response_ui": "事件只提示 ID/type/entity/version/time;每次由 REST 取真相;断线/缺号 bootstrap/refetch", + "expected_db_files": "SSE 不直接写业务表;REST 读取后前端状态更新", + "forbidden_side_effects": "不携带正文/图片/凭据,不把 runtime 事件写 ModelConfig,不抢焦点", + "evidence_files": "sse-events.json、network-timeline.json、trace.zip" + }, + { + "case_id": "TDD-WP6-DIAG-001-redacted-diagnostics", + "parent_family": "TDD-WP6-DIAG-001", + "requirement_ac_source": "PRIV-01、02、ADMIN-08、NFR-09、AC-41、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "总览/服务/存储/诊断包含所有状态;注入密钥、邮箱、路径、正文 trap", + "numbered_steps": "1. 请求各摘要;2. 复制诊断;3. 检查托盘诊断;4. 跑脱敏扫描", + "expected_response_ui": "仅显示固定非敏感字段、逻辑位置、计量和状态;复制内容一致", + "expected_db_files": "只读;可记录非敏感诊断访问/时间,不改变服务状态", + "forbidden_side_effects": "不含密钥/HMAC/邮箱/提示词/图片/绝对路径/供应商正文,不提供隐藏修复写入口", + "evidence_files": "response.json、diagnostic.txt、redaction.json、screenshots/diagnostics.png" + }, + { + "case_id": "TDD-WP2-PROJ-001-new-versus-continue", + "parent_family": "TDD-WP2-PROJ-001", + "requirement_ac_source": "PROJECT-01 至 03、GEN-04、11、AC-03、06、34", + "fixture_ids": [ + "FX-PROJECT-ACTIVE-19" + ], + "fixture_and_preconditions": "FX-PROJECT-ACTIVE-19、一个 9 图固定比例项目", + "numbered_steps": "1. 工作台新建两次;2. 在第一个项目继续生成;3. 尝试改比例;4. 补到 10 图再继续", + "expected_response_ui": "两次新建为两个 project;继续只新增 job/history且沿用比例;10 图阻断追加但可另建", + "expected_db_files": "projects +2;继续分支 project 数不变;成功 history 正确关联", + "forbidden_side_effects": "不默认追加最近项目、不创建第三项目、不改变既有项目比例、不让历史上限阻断其他新建", + "evidence_files": "response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP2-ERR-001-gateway-contract", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-13、16、17.18、AC-40、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "gateway_contract_invalid 创建前/后 fixture;另一个 contract verified 模型", + "numbered_steps": "1. 触发契约未验证/失效;2. 检查下一步仅“选择其他模型或联系管理员”;3. 显式改选已验证模型并重新确认;4. 无候选时检查联系管理员", + "expected_response_ui": "返回/保存 category=gateway_contract_invalid;已提交任务 failed;改选 verified 模型后才能新提交", + "expected_db_files": "创建前无 reserve/job;创建后 release 一次;失效模型 contract 状态按契约更新;新任务保存新模型/版本", + "forbidden_side_effects": "不提供等待、原输入/稍后重试;不让未验证模型提交;不自动切推荐或部分改写配置", + "evidence_files": "response.json、db-diff.json、external-calls.json、screenshots/gateway-contract.png" + }, + { + "case_id": "TDD-WP3-ADP-002-invalidate-revalidate", + "parent_family": "TDD-WP3-ADP-002", + "requirement_ac_source": "GEN-13、15、ADMIN-03、AC-30、40", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "三模型 verified;参数化端点/字段/参考图/响应/异步/查询/error protocol 变化", + "numbered_steps": "1. 改一个契约字段并保存;2. 尝试生成;3. 仅为该模型重跑完整矩阵;4. 再尝试", + "expected_response_ui": "变化后立即 unverified/不可新建;完整独立重验后才恢复 verified", + "expected_db_files": "新 config_version;对应 runtime/evidence 版本更新;其他模型证据不变", + "forbidden_side_effects": "不沿用旧证据、不局部 smoke 即 verified、不暗改 enabled/default/priority", + "evidence_files": "每字段 db-diff.json、contract-matrix.json、response.json" + }, + { + "case_id": "TDD-WP5-UPL-001-upload-metering", + "parent_family": "TDD-WP5-UPL-001", + "requirement_ac_source": "ADMIN-06、17.7、17.19、AC-31、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "PNG/WebP、坏 MIME/解码、稳定 ID 冲突;active/full/unavailable;原图+缩略图字节", + "numbered_steps": "1. multipart 上传并指定 part/order;2. 生成缩略图;3. 注入非法/冲突/容量状态;4. 重计量", + "expected_response_ui": "合法上传成功并可上架;非法/冲突明确失败;full/unavailable 阻断写但已有资源可读", + "expected_db_files": "原图/缩略图分别 reservation/managed_file 并计量;失败零文件/目录项", + "forbidden_side_effects": "不用扩展名替代解码、不漏计缩略、不部分覆盖稳定 ID、不在 full 写入", + "evidence_files": "response.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP4-COL-001-deterministic-palette", + "parent_family": "TDD-WP4-COL-001", + "requirement_ac_source": "COL-01 至 05、AC-15 P0-A 子集、16", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "固定解码底图、mmcq-v1、四个 P0-A 色卡", + "numbered_steps": "1. 重复提取五色;2. 调裁剪/调色/滤镜;3. 换原始底图;4. 渲染四布局;5. 监控网络", + "expected_response_ui": "同图五色/顺序确定;处理不刷新;换底图刷新;四色卡消费同一五色", + "expected_db_files": "五色快照只在底图更换提交时保存;无上传文件", + "forbidden_side_effects": "不读取处理后像素/额外图片,不联网提色,不启用白名单外色卡", + "evidence_files": "palette.json、network-timeline.json、pixel-diff.json、db-diff.json" + }, + { + "case_id": "TDD-WP6-PRIV-002-open-after-audit", + "parent_family": "TDD-WP6-PRIV-002", + "requirement_ac_source": "ADMIN-05、17.10、AC-25、50", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "active admin A 已确认 current;目标含图片和完整提示词", + "numbered_steps": "1. 列表确认无正文;2. 不带 purpose/reason 调 PRIVATE_OPEN;3. 在提交钩子确认审计落库后才释放受控读取;4. 对同目标再打开", + "expected_response_ui": "每个入口原样“打开并记录审计”;每次打开成功且各生成一条访问审计", + "expected_db_files": "每次先 append 一条含 admin/目标/时间/content_type/expires_at 的 PrivateContentAccessLog;内容本身不入审计", + "forbidden_side_effects": "UI/API 不要求目的、原因或二次确认;不预取/hover 打开;不把正文放列表、HTML、缓存或证据", + "evidence_files": "response.json、db-diff.json、ordering-trace.json、trace.zip、screenshots/open-entry.png" + }, + { + "case_id": "TDD-WP6-PRIV-002-deny-without-audit", + "parent_family": "TDD-WP6-PRIV-002", + "requirement_ac_source": "ADMIN-05、17.10、AC-25", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化未确认、普通用户/预览账号、停用 admin、审计 insert 失败", + "numbered_steps": "1. 调区域/PRIVATE_OPEN;2. 模拟审计约束失败;3. 枚举页面、SW、Cache、IndexedDB", + "expected_response_ui": "未确认 428;其他未授权 401/403/404 按冻结协议;审计失败返回非成功且无内容字节", + "expected_db_files": "未授权无访问审计;审计失败事务不留下伪成功记录,也不发放读取", + "forbidden_side_effects": "不返回正文/图片/可推导 URL;不写 AdminOperationLog 替代;不因 notice ack 省略逐次审计", + "evidence_files": "每参数 response.json、db-diff.json、cache-enumeration.json、trace.zip" + }, + { + "case_id": "TDD-WP1-DEL-001-account-delete", + "parent_family": "TDD-WP1-DEL-001", + "requirement_ac_source": "AUTH-06、AC-22、39、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "用户拥有项目、参考/生成/导出、坐标、点数、多个 session", + "numbered_steps": "1. 确认注销;2. 并发调用受保护 API;3. 尝试旧资源 ID/恢复;4. 用新邀请重新注册同邮箱", + "expected_response_ui": "注销成功后所有旧访问立即 401/404;不可恢复;新注册是全新账号", + "expected_db_files": "同事务撤销 sessions、删除身份/业务关系/未用点数并创建允许匿名事件;物理文件排队", + "forbidden_side_effects": "不延迟撤权到物理清理,不恢复旧 ID/点数/资料,不保留邮箱映射到匿名主体", + "evidence_files": "response.json、db-diff.json、worker-events.json、trace.zip" + }, + { + "case_id": "TDD-WP4-DYN-001-fixed-time", + "parent_family": "TDD-WP4-DYN-001", + "requirement_ac_source": "DYN-01 至 03、AC-14", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "可控时钟、DYN001-003/007/008/011 fixture", + "numbered_steps": "1. 插入/确认动态值;2. 推进墙钟;3. 重开;4. 导出", + "expected_response_ui": "使用插入/确认时快照,重开/导出不随当前时间变化;结果静态不可点击", + "expected_db_files": "动态实例只保存允许字段和值快照", + "forbidden_side_effects": "不保存定时器/外部链接,不在重开重新计算,不输出 GIF/视频", + "evidence_files": "canvas-state.json、clock-trace.json、pixel-diff.json" + }, + { + "case_id": "TDD-WP2-CRED-001-reserve", + "parent_family": "TDD-WP2-CRED-001", + "requirement_ac_source": "CREDIT-01 至 05、AC-03", + "fixture_ids": [ + "FX-CREDIT-ENOUGH", + "FX-MODEL-VERIFIED" + ], + "fixture_and_preconditions": "FX-CREDIT-ENOUGH、FX-MODEL-VERIFIED,无活动任务", + "numbered_steps": "1. 记录余额;2. 以成本 1 调 POST /api/v1/generations;3. 查询 /me/credits 和流水", + "expected_response_ui": "创建任务成功;UI 显示 available 9/reserved 1 和 queued;流水显示 generation_reserve", + "expected_db_files": "credit_accounts 原子 10/0→9/1;一条 reserve ledger、一个 queued job 和 outbox 同事务", + "forbidden_side_effects": "不提前生成 commit;不使总点数改变;不出现任务已建但未冻结或冻结无任务", + "evidence_files": "request.json、response.json、db-diff.json" + }, + { + "case_id": "TDD-WP0-DATA-002-no-backup-migration", + "parent_family": "TDD-WP0-DATA-002", + "requirement_ac_source": "AUTH-06、NFR-09、AC-24、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "有账号/项目/下载成品的 LocalDataRoot", + "numbered_steps": "1. 搜索 UI/API/包功能;2. 尝试导出项目包/DB/账号/点数;3. 删除根/重装;4. 启动模拟 P0-B 导入", + "expected_response_ui": "仅原图/JPG/PNG 下载可用;固定本机提示持续显示;删除/重装不可恢复;无迁移入口", + "expected_db_files": "下载目录文件保留;业务数据删除后不被重建或导入", + "forbidden_side_effects": "不自动备份、不生成迁移包、不把下载图识别为正式迁移数据", + "evidence_files": "route-inventory.json、package-scan.json、trace.zip、screenshots/fixed-copy.png" + }, + { + "case_id": "TDD-WP4-BG-002-processing-controls", + "parent_family": "TDD-WP4-BG-002", + "requirement_ac_source": "EDITOR-01 至 03、PROJECT-04、EXPORT-03、AC-07、20、23", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "固定底图和像素基线", + "numbered_steps": "1. 参数化填充/适配/裁剪/缩放/定位及亮度、对比度、饱和度、色温、锐度、预设滤镜;2. 完成;3. 撤销;4. 恢复原图;5. 导出/重开", + "expected_response_ui": "每项预览与提交值一致;完成一次撤销;导出/重开一致;恢复原图只清底图处理", + "expected_db_files": "每次提交进入保存的 Canvas JSON;取消无 DB 变化", + "forbidden_side_effects": "不影响覆盖元素,不持久化未提交预览,不用静默超范围值", + "evidence_files": "每参数 canvas-state.json、db-diff.json、pixel-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP5-CACHE-001-no-private-client-state", + "parent_family": "TDD-WP5-CACHE-001", + "requirement_ac_source": "NFR-07、17.14、AC-46、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "已访问 public/preview/private、提示词和项目;注册 SW fixture", + "numbered_steps": "1. 完成各访问;2. 离线/刷新;3. 枚举 SW registrations、Cache keys/body、IndexedDB stores/records", + "expected_response_ui": "仅 public allowlist 元数据/资源存在;SW 不拦 preview/private;离线不能恢复私有正文/草稿", + "expected_db_files": "只允许 public_asset_lru 记录;业务 DB 无变化", + "forbidden_side_effects": "不仅凭响应头判断,不缓存 no-store 内容、项目/提示词/session/验证码/未保存编辑", + "evidence_files": "cache-enumeration.json、service-worker.json、trace.zip" + }, + { + "case_id": "TDD-WP2-CRED-002-admin-adjustment", + "parent_family": "TDD-WP2-CRED-002", + "requirement_ac_source": "CREDIT-05、06、ADMIN-01、09、AC-29、50", + "fixture_ids": [ + "FX-ACTOR-ADMIN-A", + "FX-JOB-ACTIVE" + ], + "fixture_and_preconditions": "FX-JOB-ACTIVE;available 0/reserved 1;FX-ACTOR-ADMIN-A", + "numbered_steps": "1. 管理员填写原因扣减 5;2. 用同 adjustment_id 重试;3. 用户读点数", + "expected_response_ui": "首次成功,重试返回同一结果;用户显示 available -5/reserved 1;无充值入口", + "expected_db_files": "available 0→-5,reserved 保持 1;一条 admin_adjustment ledger 和一条 AdminOperationLog 同事务", + "forbidden_side_effects": "不改活动任务/冻结流水;不重复调点;不允许空原因;不使生成流程自身制造负数", + "evidence_files": "response.json、db-diff.json、screenshots/negative-balance.png" + }, + { + "case_id": "TDD-WP2-ERR-001-model-disabled", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-07、16、17.18、AC-30、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "model_disabled 创建前 fixture;参数化存在/不存在其他 enabled 模型", + "numbered_steps": "1. 触发创建前阻断;2. 检查下一步仅“选择其他模型或等待”;3. 有可用模型时显式改选并确认;4. 无可用模型时保持页面等待", + "expected_response_ui": "返回 category=model_disabled 且不创建任务;改选后才能提交;无模型时保持禁用", + "expected_db_files": "阻断分支无 job/project/reference/reserve/outbox;改选分支按新模型快照创建", + "forbidden_side_effects": "不提供联系管理员、原输入重试或稍后重试;不自动切推荐或把 runtime 状态写回配置", + "evidence_files": "response.json、db-diff.json、external-calls.json、screenshots/model-disabled.png" + }, + { + "case_id": "TDD-WP0-LOG-001-rotation-redaction", + "parent_family": "TDD-WP0-LOG-001", + "requirement_ac_source": "PRIV-01、02、NFR-09、AC-41、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "生成超过 10 MiB/10 文件/30 天日志;注入密钥、邮箱、提示词、路径和写失败", + "numbered_steps": "1. 写边界日志;2. 触发轮转/保留清理;3. 复制诊断;4. 使目录不可写并尝试 AI", + "expected_response_ui": "轮转严格为 10 MiB/10 文件/30 天;诊断只含白名单;写失败进入 unavailable 并阻断 AI", + "expected_db_files": "只更新日志文件和非敏感状态;不改业务对象", + "forbidden_side_effects": "不记录敏感正文/绝对路径/供应商原文,不在日志失败时继续 AI", + "evidence_files": "log-manifest.json、redaction.json、response.json、external-calls.json" + }, + { + "case_id": "TDD-WP0-STO-003-full-matrix", + "parent_family": "TDD-WP0-STO-003", + "requirement_ac_source": "17.19、PROJECT-04、EXPORT-06、AC-55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "managed bytes 恰为上限,status full,SQLite 可写", + "numbered_steps": "1. 保存纯 JSON project state;2. 读取/下载已有文件;3. 客户端合成并仅下载;4. 尝试 latest 保存和新生成;5. 永久删除/显式清理", + "expected_response_ui": "JSON 保存、读取、下载、仅下载、删除/清理入口可用;latest/新生成/其他二进制写为 507", + "expected_db_files": "JSON state_version 可增加;禁止项不新增 managed file/job/ledger;删除/清理只先逻辑提交/排队", + "forbidden_side_effects": "不自动删除;客户端下载不更新 latest_exports;排队不提前减少 managed bytes", + "evidence_files": "response.json、db-diff.json、fs-before.json、fs-after.json、trace.zip" + }, + { + "case_id": "TDD-WP5-CLN-001-reference-race", + "parent_family": "TDD-WP5-CLN-001", + "requirement_ac_source": "ADMIN-06、09、17.7、17.19、AC-31、50、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "查询时整批文件全部无任何发布/项目/待发布引用", + "numbered_steps": "1. 查询候选并由 admin A 创建 intent;2. 参数化确认前新增各类引用/快照过期/admin 变化;3. A 确认;4. 无竞态分支确认并运行 Worker", + "expected_response_ui": "任一新增引用整批 409 conflict+denied 审计;stale 重新查询;无竞态才整批排队/物理删除/重测", + "expected_db_files": "冲突 bytes/状态/queue 不变;成功排队不减量,物理后同事务更新并记录全阶段审计", + "forbidden_side_effects": "不让查询时有引用项入候选、不由 admin B 确认、不部分排队、不自动清理或提前减量", + "evidence_files": "每参数 response.json、db-diff.json、worker-events.json、fs-after.json" + }, + { + "case_id": "TDD-WP0-STO-001-byte-thresholds", + "parent_family": "TDD-WP0-STO-001", + "requirement_ac_source": "17.19、AC-55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "第 5.5 节七个精确字节 fixture,可写目录", + "numbered_steps": "1. 对每个值重计量;2. 读取 storage state/API/UI;3. 重复计算", + "expected_response_ui": "normal/warning/critical/full 边界逐字节正确,重复结果确定", + "expected_db_files": "只更新 measured bytes/notice/status/last_measured_at;无 managed file 变化", + "forbidden_side_effects": "不用浮点比例导致错一字节,不自动删除,不把 warning/critical 当阻断", + "evidence_files": "每值 result.json、db-diff.json、screenshots/capacity-bar.png" + }, + { + "case_id": "TDD-WP2-CRED-001-finalize-once", + "parent_family": "TDD-WP2-CRED-001", + "requirement_ac_source": "CREDIT-03、05、GEN-09、10、AC-03、04、29", + "fixture_ids": [ + "FX-JOB-ACTIVE" + ], + "fixture_and_preconditions": "参数化成功、技术失败和安全拒绝的 FX-JOB-ACTIVE", + "numbered_steps": "1. Worker 提交终态;2. 重复 callback/poll;3. 模拟 Worker 重启再处理", + "expected_response_ui": "成功为 succeeded;技术失败为 failed;安全拒绝为 rejected;用户余额/动作匹配固定类别", + "expected_db_files": "每任务恰一条 commit 或 release;reserved 恰减一次;成功才建 output/history,失败/拒绝 available 恢复", + "forbidden_side_effects": "不双重 commit/release;不在失败建 output;终态不可逆;不保存供应商原文", + "evidence_files": "worker-events.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP1-AUTH-002-invalid-invite", + "parent_family": "TDD-WP1-AUTH-002", + "requirement_ac_source": "AUTH-01、02、AC-02", + "fixture_ids": [ + "FX-INVITE-EXPIRED" + ], + "fixture_and_preconditions": "参数化 FX-INVITE-EXPIRED/DISABLED/EXHAUSTED 和不存在的邀请码", + "numbered_steps": "1. 对每个参数调 REGISTER_SEND;2. 检查 Resend mock 调用;3. 查询认证相关表", + "expected_response_ui": "返回冻结的非成功错误语义并在邀请码字段显示原因,第二阶段不展开", + "expected_db_files": "users/user_profiles/credit_accounts/privacy_consents/email_challenges/sessions 无新增;邀请码计数不变", + "forbidden_side_effects": "Resend 调用为 0;不建号、不赠点、不占名额、不写成功审计或成功提示", + "evidence_files": "每参数 response.json、db-diff.json、external-calls.json、screenshots/error.png" + }, + { + "case_id": "TDD-WP2-PROJ-003-trash-restore-purge", + "parent_family": "TDD-WP2-PROJ-003", + "requirement_ac_source": "PROJECT-03、08、AC-21", + "fixture_ids": [ + "FX-PROJECT-ACTIVE-20" + ], + "fixture_and_preconditions": "active project、FX-PROJECT-ACTIVE-20、可控 720 小时时钟", + "numbered_steps": "1. 移入回收站;2. 满 20 时恢复;3. 释放名额再恢复;4. 主动永久删除;5. 另一项目推进到 purge_at", + "expected_response_ui": "满额恢复阻断且时间不变;释放后可恢复;purge 后立即不可见/不可恢复", + "expected_db_files": "status/deleted_at/purge_at 精确;purged 撤销访问关系并排物理清理", + "forbidden_side_effects": "不提前物理结果、不因恢复重算 purge_at、不让 purged 资源可读", + "evidence_files": "response.json、db-diff.json、worker-events.json" + }, + { + "case_id": "TDD-WP2-PROJ-002-debounce", + "parent_family": "TDD-WP2-PROJ-002", + "requirement_ac_source": "PROJECT-04、06、AC-20", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "saved project version N、可控前端时钟", + "numbered_steps": "1. 1 秒内完成多次已提交编辑;2. 在首个保存请求在途时继续编辑;3. 释放响应并推进时钟;4. 撤销/重做", + "expected_response_ui": "同时最多一请求;第一份后下一请求为合并后的最新完整状态;状态 saving→saved", + "expected_db_files": "每次成功 state_version +1;最终 project_states 为最新已提交 JSON;撤销/重做也正常保存", + "forbidden_side_effects": "不逐操作并发;不保存 Fabric 私有字段;不把未提交编辑或撤销栈写浏览器持久存储", + "evidence_files": "network-timeline.json、response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP0-SUP-001-lifecycle", + "parent_family": "TDD-WP0-SUP-001", + "requirement_ac_source": "NFR-09、AC-24、41", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "真实便携包;参数化第二实例、43121 占用、API/Worker 崩溃、正常退出", + "numbered_steps": "1. 双击启动;2. 注入各故障;3. 观察托盘/重启次数;4. 退出并等待 15 秒", + "expected_response_ui": "单实例;端口占用明确失败且不换端口;有限重启/degraded/允许动作符合契约;退出后无残留", + "expected_db_files": "仅规定日志/状态记录;正常退出 flush WAL/队列;不改业务真相", + "forbidden_side_effects": "不无限重启、不杀无关进程、不弹隐藏控制台、不留下 API/Worker/端口", + "evidence_files": "process-tree.json、port.json、supervisor-events.json、screenshots/system-ui.png" + }, + { + "case_id": "TDD-WP6-AUD-001-sensitive-operations", + "parent_family": "TDD-WP6-AUD-001", + "requirement_ac_source": "ADMIN-09、17.16、AC-50", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "每类规定敏感操作 fixture,含贴纸清理各阶段和 system 配置生效", + "numbered_steps": "1. 逐类执行成功/拒绝;2. 查询 AdminOperationLog;3. 查询 PrivateContentAccessLog;4. 尝试改删/越权查看", + "expected_response_ui": "每次操作同事务形成不可变、非敏感 before/after/result;两类审计分离;只有 admin 查看", + "expected_db_files": "AdminOperationLog 条数/actor_type/ref/expires_at 正确;私有打开只写专表", + "forbidden_side_effects": "不漏失败审计、不重复保存私有正文、不记录白名单/密钥/验证码/路径、不允许人工修改删除", + "evidence_files": "operation-matrix.json、db-diff.json、redaction.json、trace.zip" + }, + { + "case_id": "TDD-WP4-VIS-001-browser-diff", + "parent_family": "TDD-WP4-VIS-001", + "requirement_ac_source": "NFR-02、AC-19、23、32", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "最终 RELEASE Chrome/Edge、固定 Canvas/字体/动态值/DPR", + "numbered_steps": "1. 等待资源稳定;2. 两浏览器截图/导出;3. 计算元素边界/显著像素;4. 人工复核已知替代", + "expected_response_ui": "结构、字体、换行、颜色、描边、装饰在第 10.1 阈值内;替代项明确标记", + "expected_db_files": "只生成视觉证据;业务状态使用同一 fixture", + "forbidden_side_effects": "不用低保真 PNG 作像素基准,不用阈值掩盖缺字体/空画布,不冒充全 P0-B", + "evidence_files": "每场景 pixel-diff.json、layout-boxes.json、manual-review.json、截图" + }, + { + "case_id": "TDD-WP7-AC-001-trace-structure", + "parent_family": "TDD-WP7-AC-001", + "requirement_ac_source": "NFR-05、52 当前 AC、109 需求 ID、13 模块、19 契约、9 错误", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "冻结 allowlist、89 family 定义、117 case 定义、页面矩阵和全部 evidence manifest", + "numbered_steps": "1. 运行 pnpm validate:tdd-trace;2. 注入缺 AC/重复主责/P0-B passed/未知需求/孤儿 family/case 缺字段/错误父族解析;3. 恢复后重跑", + "expected_response_ui": "每种破坏均非零且定位;正确状态返回 0:52 AC、109 唯一 ID、89/89 family 有 case、117 case 字段齐全", + "expected_db_files": "只生成 family/trace validation manifest;不改业务 DB", + "forbidden_side_effects": "不信任手写合计、不让 AC-08/26/37/54 计 passed、不允许未知父族、删除末段式解析或空 automation/release_gate", + "evidence_files": "family-manifest.json、trace-validation.json、negative-fixtures.json" + }, + { + "case_id": "TDD-WP1-AUTH-001-valid-register", + "parent_family": "TDD-WP1-AUTH-001", + "requirement_ac_source": "AUTH-01 至 04、AC-01", + "fixture_ids": [ + "FX-CHALLENGE", + "FX-INVITE-VALID" + ], + "fixture_and_preconditions": "FX-INVITE-VALID、FX-CHALLENGE;占名额普通账号为 9;未注册 .invalid 邮箱", + "numbered_steps": "1. 调 REGISTER_SEND;2. 读取模拟 Resend 验证码;3. 以署名、社交 ID、当前告知 version/同意和同一 registration_id 调 REGISTER_COMPLETE;4. 调普通 session", + "expected_response_ui": "两次均符合成功 Schema;注册后得到普通 audience 的 30 天会话并进入创作,点数显示 available 10/reserved 0", + "expected_db_files": "users/user_profiles/credit_accounts/privacy_consents 各新增正确关系;invite_codes.used_count +1;challenge 只消费一次;新增 session 只存 token 摘要;初始点数流水一次", + "forbidden_side_effects": "不创建 super_admin;不保存验证码明文;不重复赠点或核销邀请码;不产生第二会话/第二注册实体", + "evidence_files": "request.json、response.json、db-diff.json、trace.zip、screenshots/registered.png" + }, + { + "case_id": "TDD-WP5-WHITE-001-p0a-allowlist", + "parent_family": "TDD-WP5-WHITE-001", + "requirement_ac_source": "TEXT-01、TEXT-04、COL-01、DYN-01、DYN-08、STATIC-01、ADMIN-07、NFR-02、NFR-07、AC-32、42、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "普通用户无 grant、预览用户有单批 grant、注册全量模板", + "numbered_steps": "1. 分别读取公开/预览 manifest;2. 搜索白名单外 ID;3. 浏览所有面板", + "expected_response_ui": "普通用户仅 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 贴纸;预览只多授权批次", + "expected_db_files": "只读;授权不改变 role", + "forbidden_side_effects": "不向普通用户暴露白名单外 passed/internal,不提前开放 P0-B,不遗漏白名单;AC-54 为未来 P0-B,不进入本 case 的当前验收或 passed 关联", + "evidence_files": "manifest.json、response.json、trace.zip、screenshots/allowlist.png" + }, + { + "case_id": "TDD-WP4-BG-001-switch-background", + "parent_family": "TDD-WP4-BG-001", + "requirement_ac_source": "GEN-12、EDITOR-01、PROJECT-04、AC-07", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "画布含文字/贴纸/色卡/动态元素和已调底图,项目有第二历史图", + "numbered_steps": "1. 记录覆盖元素;2. 确认换底图;3. 检查参数/色卡;4. 撤销/重做;5. 重开", + "expected_response_ui": "覆盖层位置尺寸保留;底图处理重置;五色重提;一次撤销且重开一致", + "expected_db_files": "完整 Canvas state 自动保存并 state_version 递增", + "forbidden_side_effects": "不删除/缩放覆盖层,不保留旧调色,不产生多次撤销或改项目比例", + "evidence_files": "canvas-before.json、canvas-after.json、db-diff.json、trace.zip、pixel-diff.json" + }, + { + "case_id": "TDD-WP0-PKG-001-portable-zip", + "parent_family": "TDD-WP0-PKG-001", + "requirement_ac_source": "NFR-01、09、AC-24、41、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "release build、空白 Windows 用户目录", + "numbered_steps": "1. 生成 win-x64 ZIP/哈希/START-HERE/RELEASE;2. 解压非仓库目录;3. 离线启动;4. 扫描制品和端口", + "expected_response_ui": "包含 Dada.exe、随包 Node/native 依赖并固定 43121;说明与 RELEASE 一致", + "expected_db_files": "仅合规 LocalDataRoot 生成运行数据;制品本身不可变", + "forbidden_side_effects": "不含真实配置/凭据/用户名/LocalDataRoot,不动态换端口,不要求 Docker/安装器", + "evidence_files": "package-manifest.json、sha256.txt、package-scan.json、process-tree.json" + }, + { + "case_id": "TDD-WP4-A11Y-001-keyboard-focus", + "parent_family": "TDD-WP4-A11Y-001", + "requirement_ac_source": "NFR-06、AC-32", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "第 11.1 节全部 page_id,最长文案、弹层和 200% 缩放", + "numbered_steps": "1. 仅键盘走主流程;2. 检查 skip link/focus order/弹层返回;3. 触发 live region;4. 运行 axe;5. 测画布等价控件", + "expected_response_ui": "所有动作可达,焦点不逃逸/不被 SSE 抢走;critical/serious 为 0;状态非仅颜色", + "expected_db_files": "无非用户触发业务写;可访问性扫描只产证据", + "forbidden_side_effects": "不用正 tabindex、不隐藏主动作、不以缩小字体规避 200% 问题", + "evidence_files": "每页 axe.json、focus-trace.json、trace.zip、screenshots/200pct.png" + }, + { + "case_id": "TDD-WP1-NOTICE-001-registration-consent", + "parent_family": "TDD-WP1-NOTICE-001", + "requirement_ac_source": "AUTH-03、PRIV-04、06、AC-01、41、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "当前告知 version/effective_at/hash;缺字段、未勾选和完整参数", + "numbered_steps": "1. 展开注册第二阶段;2. 检查全部处理方/用途/本机风险;3. 未同意提交;4. 同意当前 version 注册", + "expected_response_ui": "未同意/版本错误不能注册;完整告知原样可达;成功进入创作", + "expected_db_files": "失败无 user/consent/invite/credit;成功记录 consent version/time 与账号同事务", + "forbidden_side_effects": "不省略 AI/Resend/高德、DYN004、管理员访问、注销、180 天、无备份/迁移;不预勾选", + "evidence_files": "response.json、db-diff.json、screenshots/notice-expanded.png" + }, + { + "case_id": "TDD-WP2-EXP-002-empty-latest", + "parent_family": "TDD-WP2-EXP-002", + "requirement_ac_source": "EXPORT-04、AC-23、38 P0-A 子集", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "从未导出但有成功原图的项目", + "numbered_steps": "1. 打开项目详情/latest 区;2. 请求 JPG/PNG latest;3. 使用允许动作", + "expected_response_ui": "显示“暂无导出成品”,只提供进入编辑并导出和下载原始生成图", + "expected_db_files": "latest_exports 无行且不因读取创建;原图读取只读", + "forbidden_side_effects": "不自动合成/保存默认成品,不伪造跨设备下载,不改变项目", + "evidence_files": "response.json、db-diff.json、trace.zip、screenshots/no-export.png" + }, + { + "case_id": "TDD-WP7-UI-001-page-state-evidence", + "parent_family": "TDD-WP7-UI-001", + "requirement_ac_source": "UIDesign 全页、NFR-02、06、AC-24、25、32、41、55、56", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "第 11.1 节 22 page_id 与适用状态、最终 Chrome/Edge、100%/200%", + "numbered_steps": "1. 逐非 N/A 单元运行 case;2. 截图/键盘/axe;3. 验证全局容量条;4. 对照 Pencil 相关 frame;5. 人工复核", + "expected_response_ui": "所有 UIDesign 页面/状态有实现证据且无重叠;Canvas Index 不实现;无 frame 页面不漏测", + "expected_db_files": "只生成 UI 证据;交互 DB 变化由所属业务 case 证明", + "forbidden_side_effects": "不用 PNG OCR 推文案、不把 frame 当页面全集、不以 N/A 逃避容量条/错误态", + "evidence_files": "每单元 trace.zip、axe.json、screenshots/100pct.png、screenshots/200pct.png、manual-review.json" + }, + { + "case_id": "TDD-WP4-EXP-001-format-ratio", + "parent_family": "TDD-WP4-EXP-001", + "requirement_ac_source": "EXPORT-01 至 03、AC-23", + "fixture_ids": [ + "FX-CANVAS-RATIOS" + ], + "fixture_and_preconditions": "FX-CANVAS-RATIOS,JPG quality 参数化 80/92/100,PNG", + "numbered_steps": "1. 对四比例分别导出 JPG/PNG;2. 解码像素/ICC;3. 与 Canvas fixture 比较", + "expected_response_ui": "尺寸精确为 1080×1440、1080×1080、1440×1080、1080×1920;sRGB、无水印、PNG 无损、JPG 范围/默认正确", + "expected_db_files": "纯客户端合成本身无 DB 变化;后续保存只处理同一 Blob", + "forbidden_side_effects": "不缩放到其他尺寸;PNG 时质量控件不可聚焦;不因导出改变画布/撤销栈", + "evidence_files": "每组合 result.json、image-metadata.json、pixel-diff.json、export-hash.json" + }, + { + "case_id": "TDD-WP2-ERR-001-upstream-failed", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-09、16、17.18、AC-04、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "独立 upstream_failed fixture;已提交任务临时失败", + "numbered_steps": "1. 触发临时失败;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询原任务、新任务和项目", + "expected_response_ui": "原任务 failed/category=upstream_failed;只提供“稍后重试”;动作后同项目新增一个 GenerationJob", + "expected_db_files": "原任务 release 一次且无 output;新任务独立 reserve 和关系快照", + "forbidden_side_effects": "不显示“使用原输入重试”或输入/模型修改动作;不自动重试、不创建第二项目", + "evidence_files": "response.json、worker-events.json、db-diff.json、screenshots/upstream-failed.png" + }, + { + "case_id": "TDD-WP0-STO-003-cleanup-remeasure", + "parent_family": "TDD-WP0-STO-003", + "requirement_ac_source": "ADMIN-06、17.19、AC-31、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "full;查询时全部为无引用、可清理的历史贴纸文件", + "numbered_steps": "1. 查询候选并创建 intent;2. 参数 A 在确认前给其中一项新增 active/trashed 项目、已发布版本或待发布引用,再确认;3. 参数 B 保持全部无引用并确认;4. 读取排队容量;5. Worker 物理删除并重计量", + "expected_response_ui": "A 返回 ASSET_HISTORY_REFERENCE_CONFLICT 且整批不排队;B 整批排队,排队阶段仍 full,物理删除并重计量后才按真实值恢复", + "expected_db_files": "A 只追加 result=denied 审计且 managed bytes 不变;B scheduled 前后 bytes 不变,物理成功后 files/queue/bytes/status/last_measured_at 同事务更新", + "forbidden_side_effects": "不让查询时已有引用文件进入候选;不部分排队冲突批次;不在物理删除前减量;不自动创建 intent", + "evidence_files": "每参数 response.json、db-diff.json、worker-events.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP2-BAL-001-balance-impact", + "parent_family": "TDD-WP2-BAL-001", + "requirement_ac_source": "GEN-14、15、17.17、AC-04、51", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化 model/account/unknown 影响;活动任务与空闲模型", + "numbered_steps": "1. 注入余额不足;2. 结算已提交任务;3. GET models/后台;4. 重复事件;5. 未确认恢复前尝试启用", + "expected_response_ui": "影响范围按规则;任务 failed+release 一次;runtime 不可用;恢复前操作阻断", + "expected_db_files": "GatewayBalanceState/affected models/runtime/审计更新;ModelConfig hash 不变", + "forbidden_side_effects": "不自动充值、不用 Dada 点数继续调用、不重复 release、不自动恢复或改默认/priority", + "evidence_files": "response.json、db-diff.json、worker-events.json、external-calls.json" + }, + { + "case_id": "TDD-WP2-ERR-001-safety-rejected", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-10、16、17.18、AC-04、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "供应商明确安全拒绝证据;与 reference fixture 完全分离", + "numbered_steps": "1. 触发安全拒绝;2. 检查唯一下一步为“修改提示词或参考图”;3. 不修改输入尝试继续;4. 修改提示词或参考图后提交", + "expected_response_ui": "原任务 rejected/category=safety_rejected;未修改时不能新提交;修改后同项目新增 GenerationJob", + "expected_db_files": "原任务 release 一次、无 output;修改前无新 job/reserve;修改后新任务保存修正快照", + "forbidden_side_effects": "不提供原样/稍后重试、选择模型或联系管理员;不保存违规输出;不映射为 reference_invalid", + "evidence_files": "response.json、worker-events.json、db-diff.json、screenshots/safety-rejected.png" + }, + { + "case_id": "TDD-WP2-ERR-001-gateway-balance", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-15、16、17.17、17.18、AC-40、51、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "gateway_balance_insufficient 创建前/后 fixture;明确未受影响模型集合", + "numbered_steps": "1. 触发余额不足;2. 检查下一步仅“选择未受影响模型或联系管理员”;3. 显式选择未受影响模型并重新确认;4. 无候选时检查联系管理员", + "expected_response_ui": "返回/保存 category=gateway_balance_insufficient;已提交任务 failed;只有改选未受影响模型后才能新提交", + "expected_db_files": "创建前无 reserve/job;创建后 release 一次;新任务使用改选模型快照;runtime 影响范围按 GEN-15 更新", + "forbidden_side_effects": "不提供等待、原输入/稍后重试或任意“其他模型”;不自动充值、不用 Dada 点数恢复、不回写 ModelConfig", + "evidence_files": "response.json、db-diff.json、external-calls.json、screenshots/gateway-balance.png" + }, + { + "case_id": "TDD-WP3-MDL-002-cas-conflict", + "parent_family": "TDD-WP3-MDL-002", + "requirement_ac_source": "ADMIN-03、AC-30", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "两个 admin 基于 expected N 编辑不同完整集合", + "numbered_steps": "1. A 保存为 N+1;2. B 仍以 N 保存;3. B 刷新", + "expected_response_ui": "A 成功;B 返回 412 MODEL_CONFIG_VERSION_CONFLICT 和最新安全摘要", + "expected_db_files": "仅 A 的 immutable set、pointer、审计/outbox 存在;B 零写入", + "forbidden_side_effects": "B 不覆盖 A、不自动 merge、不复用过期配置继续提交", + "evidence_files": "两份 response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP1-DEL-002-anonymous-retention", + "parent_family": "TDD-WP1-DEL-002", + "requirement_ac_source": "AUTH-06、17.10、17.11、AC-22、39", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "注销前有生成/点数事件和私有访问审计;可控 180 天时钟", + "numbered_steps": "1. 注销;2. 检查匿名字段;3. 在到期前尝试管理删除;4. 到期运行 Worker", + "expected_response_ui": "匿名事件只含允许统计;访问审计目标不可反推;到期前不可删,到期后系统清理", + "expected_db_files": "原身份关联移除;随机匿名主体无映射;expires_at 不因注销延长;到期行删除", + "forbidden_side_effects": "不保留邮箱/IP/提示词/图片/项目/job 原 ID,不让管理员手删或延长期限", + "evidence_files": "db-diff.json、retention-events.json、redaction.json" + }, + { + "case_id": "TDD-WP3-MDL-002-replace-default", + "parent_family": "TDD-WP3-MDL-002", + "requirement_ac_source": "ADMIN-03、17.2、AC-30", + "fixture_ids": [ + "FX-MODEL-VERIFIED" + ], + "fixture_and_preconditions": "FX-MODEL-VERIFIED,current set version N", + "numbered_steps": "1. 完整提交三模型集合:停用当前默认并指定另一个 enabled default;2. 带 expected N/Idempotency-Key;3. GET models", + "expected_response_ui": "保存成功,读取始终恰一个 enabled default,config_set_version=N+1", + "expected_db_files": "新 immutable set/必要 config versions、AdminOperationLog/outbox 写入,最后原子切 current pointer", + "forbidden_side_effects": "不出现可见零默认/双默认中间态;不逐行部分保存;不改无关 runtime", + "evidence_files": "request.json、response.json、db-diff.json、transaction-trace.json" + }, + { + "case_id": "TDD-WP1-AUTH-003-entry-state-matrix", + "parent_family": "TDD-WP1-AUTH-003", + "requirement_ac_source": "AUTH-01、02、05、06、AC-33", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "参数化未注册、active 已注册、deleted、suspended 邮箱;注册和登录两个入口", + "numbered_steps": "1. 每种邮箱分别调注册发码与登录发码;2. 对允许分支完成验证码;3. 检查页面不自动切入口", + "expected_response_ui": "行为严格匹配 PRD AUTH-02 状态矩阵;deleted 仅可新邀请注册;suspended 均提示联系管理员", + "expected_db_files": "只允许的分支创建 challenge/session/new user;重复注册不核销邀请码;deleted 新建主体与旧主体无关联", + "forbidden_side_effects": "不静默注册/登录转换;不恢复 deleted 数据;不因 suspended 释放名额;错误不泄漏后台白名单", + "evidence_files": "每参数 response.json、db-diff.json、trace.zip、screenshots/entry-state.png" + }, + { + "case_id": "TDD-WP4-PERF-001-budget", + "parent_family": "TDD-WP4-PERF-001", + "requirement_ac_source": "NFR-03、AC-27、32", + "fixture_ids": [ + "FX-CANVAS-50" + ], + "fixture_and_preconditions": "FX-CANVAS-50、1,407 贴纸目录、四比例导出、最终浏览器", + "numbered_steps": "1. 预热一次;2. 每预算运行五次;3. 记录 p50/p95/max/memory/DOM;4. 注入导出失败", + "expected_response_ui": "第 10.2 节所有预算通过;失败不破坏 project/latest;资源面板节点不线性增长", + "expected_db_files": "只生成性能数据;失败分支 DB/latest 不变", + "forbidden_side_effects": "不提高超时、减少元素/素材或降低尺寸掩盖失败,不用 Chromium 替代真实浏览器", + "evidence_files": "performance.json、memory.json、dom-count.json、environment.json" + }, + { + "case_id": "TDD-WP2-CRED-001-insufficient", + "parent_family": "TDD-WP2-CRED-001", + "requirement_ac_source": "CREDIT-04、AC-28", + "fixture_ids": [ + "FX-CREDIT-INSUFFICIENT" + ], + "fixture_and_preconditions": "FX-CREDIT-INSUFFICIENT,成本 1", + "numbered_steps": "1. UI 检查生成按钮;2. 直接调生成 API 防绕过;3. 查询 DB/网关 mock", + "expected_response_ui": "UI 禁用并显示 available/reserved/required/联系管理员;API 返回冻结错误语义", + "expected_db_files": "余额、ledger、job、project、reference、outbox 均无变化", + "forbidden_side_effects": "上游调用 0;无 storage reservation/staging 文件;不产生负余额", + "evidence_files": "response.json、db-diff.json、external-calls.json、screenshots/insufficient.png" + }, + { + "case_id": "TDD-WP0-STO-002-over-limit", + "parent_family": "TDD-WP0-STO-002", + "requirement_ac_source": "17.19、NFR-05、AC-55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "已计量+活动预留=5,368,709,119,预计写 2 字节", + "numbered_steps": "1. 分别尝试生成/参考图/export/贴纸原图/缩略图;2. 查询所有副作用", + "expected_response_ui": "每次 507 STORAGE_CAPACITY_EXCEEDED,返回非敏感状态和 remaining bytes", + "expected_db_files": "storage state 保持由实际量决定;无新 reservation/managed file/latest export/job/ledger/outbox", + "forbidden_side_effects": "上游调用 0;无 staging 残留;不把单个超大请求伪造成持久 full(若现有承诺仍低于上限)", + "evidence_files": "每路由 response.json、db-diff.json、fs-after.json、external-calls.json" + }, + { + "case_id": "TDD-WP2-ERR-001-reference-invalid", + "parent_family": "TDD-WP2-ERR-001", + "requirement_ac_source": "GEN-05、16、17.18、AC-04、44、53", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "格式/大小/数量和非安全型内容不可用四组 fixture;不得使用安全拒绝响应", + "numbered_steps": "1. 分别触发创建前/后无效;2. 检查唯一下一步为“更换或移除参考图”;3. 不改参考图尝试继续;4. 更换/移除后提交", + "expected_response_ui": "category=reference_invalid;未修改时不创建新任务;更换/移除后同项目新增 GenerationJob", + "expected_db_files": "创建前无 job/reserve;创建后 failed+release 一次;修正后保存新的 reference snapshot", + "forbidden_side_effects": "不提供修改提示词、重试、等待、选模型或联系管理员;不映射为 safety_rejected", + "evidence_files": "每 fixture response.json、worker-events.json、db-diff.json、screenshots/reference-invalid.png" + }, + { + "case_id": "TDD-WP4-EXP-001-confirm-pending-edit", + "parent_family": "TDD-WP4-EXP-001", + "requirement_ac_source": "PROJECT-04、06、EXPORT-03、AC-23", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "saved 画布、一次未提交文字/样式编辑、撤销栈深度 U", + "numbered_steps": "1. 打开导出;2. 看到“导出前需要提交当前修改”;3. 确认;4. 等待画布稳定并启动正常自动保存;5. 合成导出;6. 执行一次撤销", + "expected_response_ui": "导出使用确认后的状态;确认使撤销栈 U→U+1 且撤销可回到确认前;导出动作不再增加撤销项", + "expected_db_files": "确认后的完整状态进入正常保存队列;保存成功按 PROJECT-04 增加 state_version;latest 是否更新由后续保存结果决定", + "forbidden_side_effects": "不在确认时产生多条撤销;不以导出额外编辑画布;不导出确认前状态;不跳过自动保存流程", + "evidence_files": "undo-trace.json、network-timeline.json、export-hash.json、trace.zip、screenshots/pending-confirm.png" + }, + { + "case_id": "TDD-WP3-BAL-001-confirmed-recovery", + "parent_family": "TDD-WP3-BAL-001", + "requirement_ac_source": "GEN-14、15、ADMIN-09、AC-51", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "余额导致 model/account/unknown runtime unavailable;无/有真实恢复确认", + "numbered_steps": "1. 未确认时点击恢复;2. 执行非敏感健康检查;3. admin 明确确认;4. 重新计算所有阻断原因", + "expected_response_ui": "无确认失败;确认真实恢复后只解除已满足原因,仍有其他原因则继续 unavailable", + "expected_db_files": "recovery check、GatewayBalanceState/runtime/AdminOperationLog 同事务更新;config set 不变", + "forbidden_side_effects": "不自动恢复/充值,不补扣用户点数,不清除仍存在的 contract/Worker 阻断", + "evidence_files": "response.json、db-diff.json、external-calls.json、screenshots/recovery.png" + }, + { + "case_id": "TDD-WP4-DYN-003-identity-font", + "parent_family": "TDD-WP4-DYN-003", + "requirement_ac_source": "DYN-05 至 09、AC-18、19", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "用户社交 ID 含/不含 @;实例覆盖;DYN012/FONT081", + "numbered_steps": "1. 插入身份贴纸;2. 修改实例覆盖;3. 检查账号资料;4. 插入 DYN012;5. 重开/导出", + "expected_response_ui": "最多一个前导 @;实例覆盖独立且不改账号;DYN012 使用 FONT081 并明确标注替代", + "expected_db_files": "只保存实例 snapshot;UserProfile 不变", + "forbidden_side_effects": "不声称 DIN 字体、不静默回退、不执行 Lua/Prefab、不把覆盖写账号", + "evidence_files": "canvas-state.json、db-diff.json、font-load.json、pixel-diff.json" + }, + { + "case_id": "TDD-WP0-CACHE-001-public-lru", + "parent_family": "TDD-WP0-CACHE-001", + "requirement_ac_source": "NFR-07、17.14、AC-46、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "公开资源超过 157,286,400 字节;internal/private/project/提示词 fixture", + "numbered_steps": "1. 按访问顺序填充 public cache;2. 触发写前 LRU;3. 尝试缓存私有类型;4. 枚举 SW/Cache/IndexedDB", + "expected_response_ui": "总量不超上限,淘汰最久未用且当前资源可重取;私有类型均不缓存", + "expected_db_files": "public_asset_lru 只含公开元数据;业务 DB 无变化", + "forbidden_side_effects": "不缓存 internal preview、私有内容、项目、验证码、session、未保存编辑;不计用户下载", + "evidence_files": "cache-enumeration.json、lru-trace.json、trace.zip" + }, + { + "case_id": "TDD-WP0-FILE-001-atomic-commit", + "parent_family": "TDD-WP0-FILE-001", + "requirement_ac_source": "NFR-05、17.19、AC-44、55", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "受控文件驱动;合法/错误 MIME、hash、..、符号链接逃逸;各崩溃点", + "numbered_steps": "1. 流式写 staging;2. 校验/预留;3. 在改名/DB 提交前后终止;4. 重启对账", + "expected_response_ui": "非法输入拒绝;成功对象完整可读;崩溃后恢复为已提交或补偿清理,不出现半对象", + "expected_db_files": "reservation、managed_files、staging/正式文件按提交点一致;启动对账修复计量", + "forbidden_side_effects": "不接受扩展名猜 MIME、不越根、不产生可读半文件/孤儿引用/重复计量", + "evidence_files": "response.json、crash-points.json、db-diff.json、fs-before.json、fs-after.json" + }, + { + "case_id": "TDD-WP2-REF-001-reference-lifecycle", + "parent_family": "TDD-WP2-REF-001", + "requirement_ac_source": "GEN-05、PROJECT-09、AC-44、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "新上传、同项目已有 reference、他人/跨项目/撤销/已 purge reference", + "numbered_steps": "1. 提交前在页面选择/移除;2. multipart 新上传;3. 同项目复用;4. 尝试越权/失效;5. 删除项目", + "expected_response_ui": "提交前仅内存;每 job 独立关系快照;仅同 owner+project 可复用;purge 后立即不可读", + "expected_db_files": "成功建立 job reference/managed file;移除前无文件;purge 撤销关系并排清理", + "forbidden_side_effects": "不修改旧任务关系、不跨用户/项目复用、不缓存预提交图片、不暴露路径", + "evidence_files": "每参数 response.json、db-diff.json、fs-after.json、cache-enumeration.json" + }, + { + "case_id": "TDD-WP7-REL-001-final-release-record", + "parent_family": "TDD-WP7-REL-001", + "requirement_ac_source": "NFR-01、09、AC-24、41", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "最终便携 ZIP、START-HERE、SHA-256、RELEASE,真实验收电脑", + "numbered_steps": "1. 校验制品 hash;2. 导入 Windows build/Chrome/Edge 品牌、major/full version;3. 两浏览器跑全部发布 E2E/视觉/性能;4. 比较候选基线", + "expected_response_ui": "RELEASE/制品/诊断/门禁值一致;任一漂移使旧视觉/性能失去发布资格并重跑", + "expected_db_files": "只生成发布记录/证据;业务 fixture 在隔离验收根", + "forbidden_side_effects": "不缺 RELEASE、不手填假版本、不接受前一 major/Chromium、不动态换端口", + "evidence_files": "RELEASE.json、environment.json、package-manifest.json、evidence.json" + }, + { + "case_id": "TDD-WP2-PROJ-002-cas-success", + "parent_family": "TDD-WP2-PROJ-002", + "requirement_ac_source": "PROJECT-04、17.3、AC-20", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "owner project version N,完整合法 Canvas JSON", + "numbered_steps": "1. PUT state with If-Match: N;2. GET project;3. 用相同幂等请求重放", + "expected_response_ui": "首次成功并返回 saved/N+1;重放返回第一次结果", + "expected_db_files": "UPDATE ... state_version=N 命中一行;project_states 新快照,state_version 仅 +1", + "forbidden_side_effects": "不隐式上传/复制/派生资源;不泄漏或接受他人资源引用;不因重放 +2", + "evidence_files": "request.json、response.json、db-diff.json" + }, + { + "case_id": "TDD-WP4-TXT-003-catalog-search-recent", + "parent_family": "TDD-WP4-TXT-003", + "requirement_ac_source": "TEXT-01、17.6、AC-09、32", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "P0-A 32 文字模板、四分类、稳定 catalog、两个用户", + "numbered_steps": "1. 未选模板尝试加文字;2. 按显示名称搜索;3. 检查分类/顺序;4. 使用模板;5. 切换账号读最近使用", + "expected_response_ui": "只能从模板添加;四类/显示名搜索/稳定顺序正确;最近使用按账号隔离", + "expected_db_files": "只在成功使用后更新 recent_assets;未选/搜索不写项目", + "forbidden_side_effects": "不提供独立普通文字,不复用普通贴纸搜索,不跨账号共享最近项", + "evidence_files": "catalog.json、response.json、db-diff.json、trace.zip" + }, + { + "case_id": "TDD-WP1-AUTH-001-final-recheck-race", + "parent_family": "TDD-WP1-AUTH-001", + "requirement_ac_source": "AUTH-01、NFR-04、AC-01、02、45", + "fixture_ids": [ + "FX-INVITE-VALID" + ], + "fixture_and_preconditions": "FX-INVITE-VALID 已成功发码;随后参数化为邀请码耗尽/停用/过期或第 10 个名额被另一事务占用", + "numbered_steps": "1. 完成前改变可变条件;2. 调 REGISTER_COMPLETE;3. 用相同 Idempotency-Key 重试", + "expected_response_ui": "返回对应冻结错误;不建立 session,不进入创作;相同重试返回同一失败结果", + "expected_db_files": "BEGIN IMMEDIATE 后无新 user/profile/credit/consent/session;邀请码次数不增加;名额统计不变", + "forbidden_side_effects": "不出现账号已建但邀请码未核销、邀请码已核销但账号未建或初始点数孤儿流水", + "evidence_files": "response.json、db-diff.json、transaction-trace.json" + }, + { + "case_id": "TDD-WP3-MDL-001-seed-read", + "parent_family": "TDD-WP3-MDL-001", + "requirement_ac_source": "GEN-01、ADMIN-03、17.2、AC-30", + "fixture_ids": [ + "FX-MODEL-SEED" + ], + "fixture_and_preconditions": "全新迁移后的 FX-MODEL-SEED", + "numbered_steps": "1. 启动迁移;2. 调 GET /api/v1/models;3. 查集合约束", + "expected_response_ui": "返回固定三模型;Flash 唯一 configured default;priority 1/2/3;recommended null;三者 contract unverified/runtime unavailable", + "expected_db_files": "一个 current set,三个 immutable member/version,独立 runtime 行", + "forbidden_side_effects": "不把 unverified 标为 available;不生成第二默认;不把 recommended 写配置", + "evidence_files": "response.json、db-diff.json" + }, + { + "case_id": "TDD-WP5-RES-001-three-access-classes", + "parent_family": "TDD-WP5-RES-001", + "requirement_ac_source": "PRIV-02、05、17.12、13、AC-42、48", + "fixture_ids": [ + + ], + "fixture_and_preconditions": "public、internal_preview、private 资源及不同主体/grant", + "numbered_steps": "1. 请求三类 manifest/资源;2. 推导 stable ID/路径;3. 撤销 grant 后重取;4. 检查 headers/缓存", + "expected_response_ui": "public 可匿名于产品授权范围读取;preview 每次验 grant/no-store;private 仅所有者/受控 admin;越权 403/404", + "expected_db_files": "读取无业务写,访问审计仅私有完整内容规定入口产生", + "forbidden_side_effects": "不混路由/cache header,不返回路径/internal manifest,不用公开 ID 推导私有对象", + "evidence_files": "每主体 response.json、headers.json、cache-enumeration.json" + } + ] +} diff --git a/tasks.md b/tasks.md new file mode 100644 index 0000000..dba44e1 --- /dev/null +++ b/tasks.md @@ -0,0 +1,391 @@ +# Dada P0-A 开发任务计划 + +| 文档项 | 内容 | +| --- | --- | +| 文档版本 | 1.1 | +| 编写日期 | 2026-07-27 | +| 当前交付 | 仅 P0-A Windows 本机测试版 | +| 产品需求唯一权威 | `PRD.md` v1.10 | +| 功能导航 | `FeatureSummary.md` 1.1 | +| 工程基线 | `DevelopmentPlan.md` 1.1 | +| UI 基线 | `UIDesign.md` 1.0 | +| TDD 基线 | 哈希锁定的 `tdd.md`,文件内版本 1.4,89 个测试族、117 个规范 case | +| 低保真基线 | `Dada-P0A-LowFi.pen` 与 `Dada-LowFi-Preview/` | +| 机器可读任务记录 | `tasks.manifest.json`,逐任务固定 requirements、AC、DevelopmentPlan 章节、case、模块、TDD 动作、命令、证据与 UI 映射 | +| 当前状态 | 开发尚未开始;所有任务初始为 `not_started`;`RELEASE.json` 尚不存在 | +| 文档职责 | 把 WP-0 至 WP-7 拆为可执行开发任务,固定依赖、产出、测试归属和完成门禁;不修改需求、架构、UI 或测试语义 | + +## 1. 冻结输入与解释规则 + +### 1.1 输入校验 + +| 输入 | 版本 | SHA-256 | +| --- | --- | --- | +| `PRD.md` | 1.10 | `31F93674DF1A90B557FEE3AA9E74FB084E246CA8FE09F6BD4B1DC9D56D606565` | +| `FeatureSummary.md` | 1.1 | `6F80E272AAB08A5525B54501D83F16A4F6A7A170596947BBC25F1DA54F2FE844` | +| `DevelopmentPlan.md` | 1.1 | `7C579E9DF8D00DA9333F587D05FC966123437EE68575D87D73C903C1487FA9D6` | +| `UIDesign.md` | 1.0 | `0C449DF833E5471843E17EB74F5B470EBC955F117CAF1D79AC9C0EF90DE763F0` | +| `tdd.md` | 文件内版本 1.4 | `41EABEE330122FFB44752F834A97EAAE5C1CF35B99CB33B61AE6889077F96BFD` | +| `Dada-P0A-LowFi.pen` | Pencil 2.14,19 个顶层画板 | `A352C8F205E9399EB2E6306176253AF6FF65DDDBB9FDCEA2E2FE13E3C3459975` | + +用户输入把该 `tdd.md` 称为 1.3,但给出的 SHA-256 与当前文件完全一致,而文件头为 1.4。任务计划以哈希锁定的实际文件为准,因此后文统一登记为 1.4;不得把同一哈希改写为 1.3。 + +8 张低保真预览 PNG 的哈希已与 `tdd.md` 第 1.2.2 节一致。`.pen` 的 19 个顶层画板中,`Z8p3I` Canvas Index 只作设计资产索引,不属于产品页面;其余画板只约束相应页面的布局参考,不替代 `UIDesign.md` 的页面全集和文案权威。 + +### 1.2 权威与变更处理 + +1. 产品范围、状态、权限、业务规则、数据语义和 AC 结果只由 `PRD.md` 决定。 +2. `FeatureSummary.md` 只用于导航十三个功能模块、十九个契约、状态和 AC。 +3. `DevelopmentPlan.md` 固定技术栈、组件边界、API、SQLite、事务、进程、存储和 WP 顺序;任务不得重新选型或留下实现二选一。 +4. `UIDesign.md` 固定页面、控件、文案、焦点、状态和视觉规则;低保真只作布局和层级参考。 +5. `tdd.md` 固定 Red-Green-Refactor、89 个父测试族、117 个规范 case、证据和门禁。本计划不合并、删除或弱化 case。 +6. 四份全局 Markdown 任一哈希变化时,全部任务停止产出通过证据并按 `tdd.md` 标记受影响项为 `source_changed`。低保真变化按 frame 影响范围局部失效;范围不可判定时失效全部 UI、视觉和 E2E 证据。 + +## 2. 任务执行协议 + +### 2.1 状态与基本单位 + +任务状态沿用 `tdd.md`:`not_started`、`red`、`green`、`externally_blocked`、`passed`、`source_changed`。一个开发任务可以拥有多个父测试族,但每个父测试族在本计划中只有一个开发任务主责;`tdd.md` 第 6.9 节中属于该父族的全部规范 case 自动归属同一任务。 + +每个任务按固定顺序执行: + +1. 校验冻结输入哈希和上游依赖状态。 +2. 从所属规范 case 建立最小失败测试,保存 `red_reason`、禁止副作用和 Red 证据。 +3. 只实现使测试通过的最小行为,不添加 PRD 之外的入口、状态、配置或回退。 +4. 运行本层、所属 WP、跨 WP 依赖和安全脱敏回归。 +5. 在测试持续 Green 时重构,不改变 OpenAPI、Schema、事务或 UI 行为。 +6. 写入 `artifacts/tdd//` 证据;缺 Red、最终 Green 或必要人工/真实证据时不得标记 `passed`。 + +### 2.2 Definition of Ready + +任务进入 `red` 前必须同时满足: + +- 冻结哈希一致,受影响源没有未处理的 `source_changed`; +- 规范 case 的需求 ID、AC、fixture、层级、禁止副作用和证据目录已确定; +- 外部调用使用 mock;真实验证只通过显式 `pnpm validate:external` 执行; +- 测试使用临时 SQLite、临时 LocalDataRoot、独立浏览器 profile 和脱敏 fixture,不读取真实业务实例。 + +硬依赖未 Green 时,下游可以编写并执行只针对已冻结契约、独立 fixture 或 mock 接口的 Red 测试,也可以完成 mock 接线;不得进入依赖真实上游行为的最小 Green 实现,更不得标记 Green、passed 或工作包完成。硬依赖 Green 是从 Red 进入 Green 的前置条件,不是建立 Red 的前置条件。 + +### 2.3 Definition of Done + +单个任务达到 `green` 必须满足: + +- 所属父测试族及全部规范 case 已保存可复现 Red 和最终 Green 结果; +- 所有固定层级分别通过,不能用 E2E 代替 DB/API,也不能用 mock 代替 EXT-REAL; +- 状态、响应、持久化、文件、UI 和禁止副作用均有断言; +- `evidence_refs` 完整且脱敏扫描通过; +- 所属 WP 回归和本计划第 5 节的跨包契约回归通过; +- 没有 P0-B、P1 或明确不做能力混入。 + +任务含 EXT-REAL 或 MANUAL 层级时,本地自动化 Green 后可以进入 `externally_blocked`;只有真实/人工证据齐全才为 `passed`。WP 只有在本 WP 所有任务达到其规定门禁、且“不能提前宣告”条件全部消除后才能完成。 + +### 2.4 仓库级测试接口前置 + +WP-0 必须建立并持续维护以下脚本:`pnpm test:unit`、`test:integration`、`test:api`、`test:worker`、`test:e2e`、`test:visual`、`test:performance`、`test:security`、`test:package`、`validate:tdd-trace`、`test:all` 和 `validate:external`。任一失败返回非零;普通 CI 不运行真实外部验证。 + +## 3. 依赖顺序与并行边界 + +| 阶段 | 可执行工作 | 硬门禁 | +| --- | --- | --- | +| A | WP-0 全部任务 | 无上游;WP-0 Green 后才开放业务实现完成声明 | +| B | WP-1;WP-5 的只读编译器、catalog 和资源路由骨架可启动 | WP-1 依赖 WP-0;WP-5 依赖 WP-0 | +| C | WP-2;WP-5 继续 | WP-2 依赖 WP-0、WP-1 | +| D | WP-3、WP-4、WP-5 可并行;WP-4 最终素材视觉集成等待 WP-5 | WP-3/WP-4 依赖 WP-2;WP-5 最终 renderer 集成依赖 WP-4 | +| E | WP-6 | 依赖 WP-1、WP-2、WP-5;模型页最终状态另依赖 WP-3 | +| F | WP-7 | WP-0 至 WP-6 Green、三个外部阻塞具备真实验证条件 | + +依赖未 Green 时,下游可以先写 Red、Schema fixture 或 mock 接线,但不能标记 Green。AI 网关、Resend、高德和最终真实 Chrome/Edge 是发布外部门禁,不阻止其本地状态机和 mock 分支开发,但不得伪装成已完成集成。 + +## 4. 可执行开发任务 + +### 4.1 WP-0 基础工程与本机边界 + +主责范围:PRIV-01、PRIV-02、NFR-01、NFR-07、NFR-09;AC-24、AC-46、AC-55、AC-56。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP0-01` 工程骨架与冻结依赖 | 无 | 建立 pnpm monorepo、唯一 lockfile、React/Vite/TypeScript、Fabric、Fastify、TypeBox、Drizzle/better-sqlite3、Node Worker 和 .NET 8 Supervisor 最小可运行骨架;接通第 2.4 节脚本入口 | `TDD-WP0-DEP-001` | 精确版本安装、类型检查、React/Fabric 渲染、Fastify、native 模块和随包 Node 预检通过;失败时先修订 DevelopmentPlan,不得私自换栈 | +| `TASK-WP0-02` Schema、OpenAPI 与事件基线 | `TASK-WP0-01` | 建立 TypeBox 单一 Schema 来源、OpenAPI 3.1、生成客户端、统一错误信封、correlation ID、SSE 非敏感事件和 REST refetch/bootstrap | `TDD-WP0-API-001`、`TDD-WP0-EVT-001` | OpenAPI snapshot 与运行校验一致;事件不承载业务真相或私有正文;断线/缺号恢复通过 | +| `TASK-WP0-03` 回环访问与浏览器硬门禁 | `TASK-WP0-02` | 固定 `127.0.0.1:43121`、Host/Origin/CORS 防护、最小 gate shell、UA-CH 交叉校验、24 小时短期支持 Cookie、全产品路由前置 426 门禁 | `TDD-WP0-BND-001`、`TDD-WP0-BRW-001`、`TDD-WP0-BRW-002` | 局域网/其他 Host/Origin、非 Windows、其他品牌、错误 major、UA-CH 缺失/冲突和直调 API 均不可进入;支持分支最终 passed 等待 WP-7 真实记录 | +| `TASK-WP0-04` LocalDataRoot 与本机数据边界 | `TASK-WP0-01` | 建立目录布局、路径规范化、资源 ID 隔离、data_missing、只读素材根校验、无备份/无归档/无迁移边界和固定本机风险提示接线 | `TDD-WP0-DATA-001`、`TDD-WP0-DATA-002` | 违规目录、符号链接和路径逃逸拒绝;已初始化根缺失不自动重建;浏览器不见绝对路径;产品无业务导入/项目包/恢复入口 | +| `TASK-WP0-05` 容量状态、预留与文件一致性 | `TASK-WP0-02`、`TASK-WP0-04` | 实现 LocalBackendStorageState、精确阈值、storage reservation、staging 流式校验、同卷原子改名、managed file、补偿队列、启动对账和物理删除后重测 | `TDD-WP0-STO-001`、`TDD-WP0-STO-002`、`TDD-WP0-STO-003`、`TDD-WP0-FILE-001` | 写前只用 `>` 拒绝,恰好 5 GB 写入成功后 full;超限零副作用;full/unavailable 动作不同;未物理删除前不减字节 | +| `TASK-WP0-06` 公开客户端缓存 | `TASK-WP0-02`、`TASK-WP0-04` | 建立 150 MiB public allowlist LRU、Cache Storage/IndexedDB 元数据、写前淘汰和 Service Worker 路由边界 | `TDD-WP0-CACHE-001` | 逐场景枚举实际浏览器存储;internal/private/项目/提示词/未保存草稿均不进入持久缓存 | +| `TASK-WP0-07` Supervisor、凭据和运行诊断 | `TASK-WP0-01`、`TASK-WP0-04`、`TASK-WP0-05` | 实现单实例 mutex、固定端口检查、API/Worker 启停与有限重启、15 秒退出、Credential Manager 匿名管道注入、托盘状态、离线配置命令和脱敏诊断 | `TDD-WP0-SUP-001`、`TDD-WP0-SEC-001` | 第二实例、端口占用、startup/api/worker degraded、凭据缺失/清除/重启和子进程残留均有确定行为;不动态换端口、不泄露凭据 | +| `TASK-WP0-08` 日志、轮转和不可写联动 | `TASK-WP0-05`、`TASK-WP0-07` | 建立 API/Worker/Supervisor JSONL 白名单日志、10 MiB/10 文件/30 天轮转、诊断复用脱敏器和日志不可写状态联动 | `TDD-WP0-LOG-001` | 日志不含密钥、邮箱、提示词、图片、绝对路径或供应商原文;日志不可写进入 unavailable 并阻断 AI | +| `TASK-WP0-09` 隔离 CI 与便携包 | `TASK-WP0-01` 至 `TASK-WP0-08` | 配置 Windows Gitea Runner 隔离流水线;生成 Dada.exe、随包 Node、API/Worker、迁移、资源元数据、`RELEASE.json` 占位规则、`START-HERE.txt`、ZIP 和 SHA-256 | `TDD-WP0-PKG-001` | CI 不读真实凭据、LocalDataRoot 或 3.57 GB 归档;包固定 43121、零真实配置/路径;正式 RELEASE 记录在 WP-7 冻结 | + +WP-0 结束时运行:`test:unit`、`test:integration`、`test:api`、`test:worker`、`test:security`、`test:package` 和所属 E2E。真实浏览器支持记录尚未冻结时,AC-24 只能保持 Green 或 externally_blocked,不能 passed。 + +### 4.2 WP-1 认证、权限、数据模型与审计基础 + +主责范围:AUTH-01 至 AUTH-07、PRIV-03、PRIV-04、PRIV-06、NFR-04;AC-01、AC-02、AC-22、AC-33、AC-39、AC-45、AC-49。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP1-01` 认证数据与邀请注册事务 | WP-0 | 建立 UserProfile、邀请码、验证码挑战、普通账号初始点数/registration_grant、幂等键与事务;接入 Resend mock | `TDD-WP1-AUTH-001`、`TDD-WP1-AUTH-002` | 发码前/建号前复核;有效注册一次提交;无效、过期、停用、耗尽邀请零副作用 | +| `TASK-WP1-02` 登录状态机、会话与登录画面 | `TASK-WP1-01` | 实现注册/登录分离入口、未注册/已注册/已注销/suspended 状态、10 分钟验证码、60 秒重发、30 天 opaque session、CSRF 和退出/暂停/注销撤销;按 `Z0pf8` 落实普通用户登录布局、艺术带、管理员文字入口、固定本机提示和焦点 | `TDD-WP1-AUTH-003`、`TDD-WP1-AUTH-004` | 六类身份状态不静默换流程;验证码不可复用;会话撤销即时生效;普通与管理员 audience 分离;登录页状态与布局证据齐全 | +| `TASK-WP1-03` 注册展开态、内测告知与账号名额 | `TASK-WP1-01`、`TASK-WP1-02` | 实现版本化内测告知、必选确认、资料字段、active+suspended 普通账号 10 人上限、deleted 释放和 super_admin 不占名额;按 `DVPM8` 落实邀请/邮箱只读摘要、资料、告知和注册主动作 | `TDD-WP1-NOTICE-001`、`TDD-WP1-SLOT-001` | 告知覆盖 PRD 固定事项;未同意不建号;第 11 个普通账号被阻断;多个同权限 super_admin 合法;注册展开态状态与布局证据齐全 | +| `TASK-WP1-04` 管理员白名单与安全配置生效 | WP-0、`TASK-WP1-01`、`TASK-WP1-02` | 实现 HMAC 管理员白名单、独立管理员认证、多管理员、安全配置 revision、原子应用/回滚、system 审计和离线恢复边界 | `TDD-WP1-ADM-001`、`TDD-WP1-CFG-001` | 非白名单发码前拒绝;普通账号不能原地提升;配置失败不部分生效;部署人员不成为产品角色或审计 actor | +| `TASK-WP1-05` 注销、匿名保留与物理清理 | `TASK-WP1-01` 至 `TASK-WP1-04` | 实现注销事务、立即撤权与断链、邮箱释放、匿名事件、物理清理队列和最长 180 天保留 | `TDD-WP1-DEL-001`、`TDD-WP1-DEL-002` | 旧数据不可恢复;原邮箱只能新邀请新建;匿名事件不可反查;注销不延长私有访问审计期限 | +| `TASK-WP1-06` 两类审计基础不可变性 | WP-0、`TASK-WP1-04`、`TASK-WP1-05` | 建立 AdminOperationLog 与 PrivateContentAccessLog 表、append-only trigger、合法 retention/privacy purge 作用域和非敏感摘要 | `TDD-WP1-AUD-001` | 普通连接不能改删;两类日志不混用;正文、图片、密钥和路径不进入审计 | + +WP-1 的认证 UI 同步实现 `user-auth` 与 `admin-auth` 的正常、加载、失败和禁用状态。Resend mock Green 不等于正式认证 passed;真实送达与正式账号链由 WP-7 完成。 + +### 4.3 WP-2 项目、点数与 GenerationJob + +主责范围:CREDIT-01 至 CREDIT-06、PROJECT-01 至 PROJECT-09、GEN-03 至 GEN-06、GEN-11、GEN-12、GEN-14、GEN-16、NFR-05;AC-03 至 AC-06、AC-20、AC-21、AC-28、AC-29、AC-34、AC-35、AC-36 P0-A 子集、AC-38 P0-A 子集、AC-43、AC-44、AC-51、AC-53。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP2-01` 项目、历史、失败草稿与空工作台 | WP-0、WP-1 | 实现新建项目、项目内追加、固定比例、20 active、10 成功图、默认命名/重命名、failed_empty 重试和批量入回收站;按 `WbDYT` 落实无项目工作台艺术带、生成表单和空任务区 | `TDD-WP2-PROJ-001`、`TDD-WP2-PROJ-005` | 新建/追加/上限不混淆;重试只新建任务;有成功图项目不能进入 failed_empty 批量操作;艺术带仅在无项目工作台出现 | +| `TASK-WP2-02` 自动保存与多标签冲突 | `TASK-WP2-01` | 实现 Dada Canvas Schema、1 秒 debounce、单请求在途、最新快照合并、If-Match CAS、dirty/saving/saved/failed/conflicted、离开守卫和一次冲突本地导出 | `TDD-WP2-PROJ-002`、`TDD-WP2-PROJ-004` | 旧标签不能覆盖或另存副本;冲突页只读且仅一次本地导出,不写 latest;刷新只恢复 saved | +| `TASK-WP2-03` 回收站、恢复与 purge | `TASK-WP2-01`、WP-0 文件清理 | 实现 trashed、精确 `+720h` purge_at、20 active 恢复限制、立即撤权和异步物理清理 | `TDD-WP2-PROJ-003` | 满 20 时保持 trashed;purged 后立即不可读;删除排队不伪造容量下降 | +| `TASK-WP2-04` 点数账户、流水与管理员调点 | WP-1、WP-0 SQLite | 实现 available/reserved、reserve/commit/release、不可变 ledger、operation_key、余额不足和带原因的 admin adjustment | `TDD-WP2-CRED-001`、`TDD-WP2-CRED-002` | 生成流程 available 不为负;结算一次;调点只改 available、可为负,reserved 不变,流水和审计同事务 | +| `TASK-WP2-05` 生成提交、单任务、参考图与生成中工作台 | `TASK-WP2-01`、`TASK-WP2-04`、WP-0 存储 | 实现 multipart 提交、config/cost 复核、`BEGIN IMMEDIATE`、活动任务部分唯一索引、参考图内存态/流式提交/关系快照、幂等和 412 重确认;按 `d99Lo` 落实生成中工作台、当前任务、冻结点数、critical 容量条和禁用提交 | `TDD-WP2-GEN-001`、`TDD-WP2-GEN-003`、`TDD-WP2-REF-001` | 同账号只建一个活动任务;第二提交返回当前任务且不建项目/关系/冻结;过期配置和无效参考图零持久副作用;生成中页面不显示假进度且提交动作按状态禁用 | +| `TASK-WP2-06` 任务终态、九类错误与网关余额影响 | `TASK-WP2-04`、`TASK-WP2-05`;WP-3 Adapter 接口 mock | 实现成功/失败/rejected、输出落盘与一次结算、固定九类错误、用户动作、GatewayBalanceState model/account/unknown 影响和 pending_manual_review | `TDD-WP2-GEN-002`、`TDD-WP2-BAL-001`、`TDD-WP2-ERR-001` | `reference_invalid` 与 `safety_rejected` 分离;供应商原文不泄露;runtime 变化不回写 ModelConfig;用户点数只释放一次 | +| `TASK-WP2-07` 原图与 latest export 服务 | `TASK-WP2-01`、`TASK-WP2-02`、WP-0 存储 | 实现历史原图下载、latest JPG/PNG 资源关系、同 Blob 上传、后端失败矩阵和无导出成品空态 | `TDD-WP2-EXP-001`、`TDD-WP2-EXP-002` | 客户端下载与后端保存结果独立;失败保留旧 latest;从未导出不自动合成;不承诺跨设备下载 | + +WP-2 同步交付 `workspace`、`current-task`、`projects`、`project-detail`、`credits` 和 `settings` 的业务接线。真实模型完成态与真实点数结算仍依赖 WP-3,不得用 Adapter mock 宣告 AC-40。 + +### 4.4 WP-3 三模型契约验证与适配 + +主责范围:GEN-01、GEN-02、GEN-07 至 GEN-10、GEN-13、GEN-15、ADMIN-03;AC-30、AC-40。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP3-01` ModelConfig 集合与原子替换 | WP-2 | 实现三模型固定 seed、immutable config_version/config_set、恰好一个 enabled default、唯一正整数 priority、完整集合 CAS、替代默认错误和审计 | `TDD-WP3-MDL-001`、`TDD-WP3-MDL-002` | 初始 1/2/3 优先级;缺失/非正/重复/零或多默认整批拒绝;切换无可见中间态;失败原集合不变 | +| `TASK-WP3-02` 独立 runtime 与推荐模型 | `TASK-WP3-01`、WP-2 余额状态 | 实现 runtime reason/version、configured default/runtime/recommended 三层读取、空推荐、人工恢复和余额影响审计 | `TDD-WP3-MDL-003`、`TDD-WP3-BAL-001` | 降级/恢复不改 enabled/default/priority;推荐只从可用候选派生;成本核对不补扣用户点数 | +| `TASK-WP3-03` 三个独立 Adapter 与契约证据 | `TASK-WP3-01`、WP-2 Generation 接口 | 分别实现三个 Adapter、纯文字/参考图/四比例、同步/异步、响应、一图、错误映射;配置字段变化令对应模型 unverified;提供 mock 和受控真实验证 | `TDD-WP3-ADP-001`、`TDD-WP3-ADP-002` | 三模型 fixture/证据不得共用;完整重验前不可恢复 verified;真实证据未齐时任务保持 externally_blocked | +| `TASK-WP3-04` Worker 租约、崩溃恢复与结算接线 | `TASK-WP3-03`、`TASK-WP2-06` | 实现领取、租约、心跳、poll、崩溃恢复、无安全引用的 unknown_retryable、一次 release 和人工成本核对 | `TDD-WP3-WRK-001` | 有上游引用只 poll、不重新 submit;未知结果终止用户任务且不双结算;Worker degraded 禁止新任务 | + +WP-3 本地 mock 全 Green 后,如任一模型缺最后一次配置对应的真实证据,整个 WP-3 保持 `externally_blocked`,AC-30 的纯配置行为可 Green,AC-40 不得 passed。 + +### 4.5 WP-4 桌面编辑器与导出 + +主责范围:EDITOR-01 至 EDITOR-09、TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、EXPORT-01 至 EXPORT-06、NFR-03、NFR-06;AC-07、AC-09 至 AC-12、AC-14、AC-16 至 AC-19、AC-23 P0-A 子集、AC-27、AC-32。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP4-01` 编辑器壳、Canvas Schema 与底图 | WP-2;使用 WP-5 稳定 fixture | 按 UIDesign/低保真实现三栏编辑器、顶部/底部状态、Fabric 转换边界、底图填充/适配/裁剪/调色/滤镜/恢复原图和换底图 | `TDD-WP4-BG-001`、`TDD-WP4-BG-002` | 底图参数可预览、提交、撤销、保存、重开和导出一致;换图保留覆盖层、重置底图处理并刷新色卡 | +| `TASK-WP4-02` 画布选择、变换与普通贴纸 | `TASK-WP4-01` | 实现循环选择、长按候选、多选/框选、移动/缩放/旋转/翻转/透明度/复制/层级/吸附、撤销重做和 50 元素双端限制 | `TDD-WP4-CAN-001`、`TDD-WP4-STK-001` | 50 元素可编辑保存导出,第 51 个被阻断;无图层面板、隐藏、锁定、编组;动态 UI 不改变工具尺寸 | +| `TASK-WP4-03` 文字模板、字体与属性 | `TASK-WP4-01`;WP-5 模板/字体 fixture | 实现必须先选模板、四类/显示名搜索/catalog 顺序/账号最近使用、多行内容、模板切换、FontFace ready、字号/缩放、填充/描边/背景/对齐/行距/字距和一次提交撤销 | `TDD-WP4-TXT-001`、`TDD-WP4-TXT-002`、`TDD-WP4-TXT-003` | 文本/换行/变换按规则保留;真实字体缺失使模板 unavailable;不提供普通文字或普通贴纸关键词搜索;不静默回退 | +| `TASK-WP4-04` 色卡与动态贴纸 provider | `TASK-WP4-01`;WP-5 声明式资源 | 实现 `mmcq-v1` 确定五色、四个色卡 renderer、时间/地点/身份 provider、DYN004 先确认后定位、坐标保存/清理、DYN012 FONT081 标记 | `TDD-WP4-COL-001`、`TDD-WP4-DYN-001`、`TDD-WP4-DYN-002`、`TDD-WP4-DYN-003` | 色卡只读原始底图且网络上传为 0;时间值重开不漂移;拒绝定位调用为 0;身份前缀一个 `@`;DYN012 不静默换字体 | +| `TASK-WP4-05` 客户端导出与结果弹层 | `TASK-WP4-01` 至 `TASK-WP4-04`、`TASK-WP2-07` | 实现四比例真实像素、sRGB、JPG 80-100/默认 92、PNG 无损、无水印、未提交修改确认、同一 Blob 下载/上传和四结果组合 UI | `TDD-WP4-EXP-001` | 确认未提交修改只形成一次撤销并进入自动保存;导出本身不额外建撤销;后端失败不撤回已成功下载 | +| `TASK-WP4-06` 键盘、焦点与语义可访问性 | `TASK-WP4-01` 至 `TASK-WP4-05` | 落实 skip link、焦点顺序、ARIA/live region、弹层焦点/返回、画布外等价控件、200% 缩放和 axe | `TDD-WP4-A11Y-001` | axe critical/serious 为 0;状态不只靠颜色;异步更新不抢焦点;最长文案、弹层和主动作可达 | +| `TASK-WP4-07` 视觉与性能预算 | `TASK-WP4-01` 至 `TASK-WP4-06`;最终素材依赖 WP-5 | 建立固定 Canvas/字体/动态值、Chrome/Edge 截图 diff、50 元素交互、自动保存、编辑器打开、导出、内存和 DOM 性能 harness | `TDD-WP4-VIS-001`、`TDD-WP4-PERF-001` | 边界偏差、显著像素和全部性能预算符合 tdd;已知替代人工复核;不得通过放宽超时、减元素或降尺寸掩盖失败 | + +WP-4 以 `editor` 和 `export` 为主 UI 单元,并回归 `workspace`、`project-detail` 的进入/返回、保存和 latest 状态。占位字体、未校验模板或只在一个浏览器通过时不能完成。 + +### 4.6 WP-5 素材转换、发布与 P0-A 白名单 + +主责范围:STATIC-01 至 STATIC-04、ADMIN-06、ADMIN-07、PRIV-05、NFR-02;AC-13、AC-15 P0-A 子集、AC-31、AC-42、AC-48。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP5-01` 只读素材编译器 | WP-0 | 建立 TypeScript CLI,只读读取规范 manifest/catalog/metadata,输出 TemplateRenderModel、版本化元数据、最小 derived 产物和源前后校验报告;不执行 Lua/Prefab | `TDD-WP5-MAN-001` | 源文件哈希/时间不变;不读证据目录;不复制全库或形成等价派生副本;内容哈希去重复用 | +| `TASK-WP5-02` 普通贴纸 catalog 与虚拟目录 | `TASK-WP5-01` | 构建 part1-25 的 1,407 项 StaticStickerCatalog,保留三组重复、稳定 ID/顺序/哈希/尺寸/MIME/缩略图引用;接入虚拟列表和懒加载 | `TDD-WP5-CAT-001` | 计数、part、重复和顺序精确;原图按需加载;DOM 只保留视口和两屏缓冲 | +| `TASK-WP5-03` 复杂资源注册与 P0-A 白名单 | `TASK-WP5-01`;与 WP-4 renderer 契约联调 | 导入注册 332 文字、86 字体项、16 色卡、35 动态;公开 manifest 只启用 32 文字、实际字体+FONT081、COLOR001/002/008/016、10 动态和 1,407 普通贴纸 | `TDD-WP5-WHITE-001`、`TDD-WP5-COL-001` | 白名单外复杂素材不对普通用户可见;四种色卡消费同一五色;色卡/动态不计入 25 part;不把 full_p0 分批公开 | +| `TASK-WP5-04` Immutable manifest 与三类资源隔离 | `TASK-WP5-01` 至 `TASK-WP5-03`、WP-0 gate/cache | 实现 release_version、manifest/file SHA-256、public/preview/private 三类路由、鉴权、no-store/immutable 缓存和不可推导资源 URL | `TDD-WP5-RES-001`、`TDD-WP5-CACHE-001` | public manifest 不含 internal/private;浏览器不见绝对路径;SW/Cache/IndexedDB 实际枚举无私有或预览内容 | +| `TASK-WP5-05` 后台普通贴纸上传与版本发布 | `TASK-WP5-02`、`TASK-WP5-04`、WP-0 存储 | 实现 PNG/WebP 流式校验、stable ID/part/顺序、原图与缩略图独立预留/计量、immutable release 切换和旧版本保留 | `TDD-WP5-UPL-001` | 两类文件均计入 5 GB;full/unavailable 阻断;恰好等于上限允许提交后 full;不把既有归档复制进 managed-assets | +| `TASK-WP5-06` 历史贴纸显式清理 | `TASK-WP5-05`、WP-2 project_asset_refs、WP-1 审计 | 实现候选查询、同管理员确认、短期 snapshot、`BEGIN IMMEDIATE` 全引用复核、整批拒绝/排队、Worker 物理删除、重计量和全阶段审计 | `TDD-WP5-CLN-001` | 有任一 release/active/trashed/待发布引用则整批拒绝;排队不减容量;无自动清理;物理删除和重测后才恢复 | +| `TASK-WP5-07` 内部预览授权生命周期 | `TASK-WP5-03`、`TASK-WP5-04`、WP-1 会话 | 实现批次、AssetPreviewGrant、active/revoked/expired、每次授权校验、随机 manifest item ID、撤销即时阻断和既有项目按版本渲染 | `TDD-WP5-PREV-001` | 授权不改变普通用户角色;不出现后台能力;撤销/到期阻断新取用但不破坏已保存项目 | + +真实约 3.57 GB 归档只在受控 release job 只读校验,日常 CI 使用小型合成 fixture。WP-5 的最终人工视觉通过依赖 WP-4 renderer;两者可并行开发但不能相互用占位实现宣告完成。 + +### 4.7 WP-6 运营后台与内部运营控制 + +主责范围:ADMIN-01、ADMIN-02、ADMIN-04、ADMIN-05、ADMIN-08、ADMIN-09、NFR-08;AC-25、AC-47、AC-50、AC-52。模型配置、点数和素材能力分别复用 WP-3、WP-2、WP-5,不转移唯一主责。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Green / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP6-01` 后台壳、权限与安全摘要 | WP-1、WP-2、WP-5 | 按 UIDesign 实现独立 `/admin` 路由域、侧栏/总览、用户、邀请码、点数、素材、预览、生成记录入口和 active super_admin 服务端授权 | `TDD-WP6-ADM-001` | 普通/预览账号无法进入;管理员与普通 session 域分离;默认列表只含脱敏摘要;无敏感配置回显 | +| `TASK-WP6-02` 私有内容告知与逐次访问 | `TASK-WP6-01`、WP-1 UserProfile/审计 | 实现当前 notice version bootstrap、身份+版本持久 ack、428 区域门禁、升版重阻断、“打开并记录审计”和先审计后返回内容 | `TDD-WP6-PRIV-001`、`TDD-WP6-PRIV-002` | 跨标签、重启、重登和 Chrome/Edge 同版本不重复;hover/预取不打开;每次点击独立记录;审计失败不返回内容;不要求逐次填写原因 | +| `TASK-WP6-03` 免费服务硬停止与恢复 | `TASK-WP6-01`、WP-1/2 事务基础 | 实现 Resend 每日/月度、高德月度用量领取,paused_quota/provider、上限只可下调、健康检查和 super_admin 人工恢复 | `TDD-WP6-SVC-001`、`TDD-WP6-SVC-002` | 第 81/1001 次不调用;不得提高固定上限、启用收费、自动恢复/扩容;高德暂停只影响 DYN004 自动定位 | +| `TASK-WP6-04` 敏感操作审计与保留 | `TASK-WP6-01` 至 `TASK-WP6-03`、WP-5 清理 | 把用户、点数、邀请、模型、素材、预览、服务、余额恢复和贴纸清理各阶段纳入同事务 AdminOperationLog;实现 180 天 Worker 清理 | `TDD-WP6-AUD-001` | 两类审计严格分离、180 天内不可改删、贴纸清理请求/复核/拒绝或排队/物理结果齐全且不含正文 | +| `TASK-WP6-05` 模型/服务/存储状态与诊断 | `TASK-WP6-01`、WP-0 诊断、WP-3 runtime | 实现总览、模型、服务与存储页面的 configured default/runtime/recommended 三列、Worker/网关/Resend/高德/storage 状态、REST refetch 和复制诊断 | `TDD-WP6-STATE-001`、`TDD-WP6-DIAG-001` | 推荐可 null;runtime 变化不回写配置;只显示稳定非敏感字段和逻辑位置;无密钥、邮箱、路径、正文或供应商原文 | + +WP-6 必须覆盖 `admin-overview`、`admin-users`、`admin-invites`、`admin-models`、`admin-assets`、`admin-preview`、`admin-generations`、`admin-services-storage` 和 `admin-audit`。后台按钮的禁用态不替代服务端授权、CAS、事务或审计。 + +### 4.8 WP-7 P0-A 验收准备与发布 + +主责范围:PRD 第 22 节全部 52 项当前 AC 和第 23 节发布门槛;产品 AC 唯一主责仍留在 WP-0 至 WP-6,WP-7 只主责 AC-41 并汇总发布证据。 + +| 任务 | 硬依赖 | 实现与产出 | 唯一主责测试族 | Passed / 禁止提前宣告 | +| --- | --- | --- | --- | --- | +| `TASK-WP7-01` 候选制品与候选浏览器记录 | WP-0 至 WP-6 Green | 从候选便携包和当前安装的真实 Chrome/Edge 可执行文件生成 `artifacts/tdd//release-candidate.json`,记录 Windows build、品牌、major、full version、记录时间、候选制品哈希和固定端口;生成候选 ZIP 与候选 `START-HERE.txt`,但不写最终 `RELEASE.json` | 支撑任务,不新增父测试族;最终发布验收由 `TASK-WP7-07` 主责 | 候选记录字段完整、脱敏且与候选制品一致;只作为后续精确环境输入,不能标记发布冻结或替代最终 RELEASE 证据 | +| `TASK-WP7-02` 三模型真实契约验收 | `TASK-WP7-01`、WP-3 Green、受控凭据 | 对三个模型分别运行纯文字、参考图、四比例、同步/异步、响应、九类错误、结算和契约变化重验,保存脱敏证据 | `TDD-WP7-EXT-001` | 每个模型独立证据齐全且匹配最后配置;任一失败保持 externally_blocked;不共享 verified 标记 | +| `TASK-WP7-03` Resend 真实发布门槛 | `TASK-WP7-01`、WP-1/WP-6 Green、受控域名 | 核验 SPF/DKIM、免费规则;QQ/163/企业邮箱各 20 封至少 19 封在 2 分钟内送达;用正式链完成普通/管理员认证 | `TDD-WP7-EXT-002` | 人工统计脱敏;开发预置账号和 mock 不能作为证据;未达门槛不启动实际 P0-A 验收 | +| `TASK-WP7-04` 高德真实发布门槛 | `TASK-WP7-01`、WP-4/WP-6 Green、受控 Key | 核验定位/逆地理、QPS、白名单、安全限制、每月 1,000 次硬停止和 DYN004 确认流程 | `TDD-WP7-EXT-003` | 真实测试坐标不关联真实项目;不发第 1001 次;不启用付费或自动扩容;失败保持 externally_blocked | +| `TASK-WP7-05` 候选精确环境 UI、视觉、性能与可访问性验收 | `TASK-WP7-01` 至 `TASK-WP7-04` | 严格使用候选记录中的两个真实 full version,在 1920×1080、DPR 1、100%/200% 下重跑 AC-24、AC-32、全部 E2E、视觉、性能、下载、文件选择和逐个非 N/A 页面状态;按 frame 对照低保真 | `TDD-WP7-UI-001` | 22 个 coverage unit 全部有证据;候选环境与记录任何漂移均使本任务重跑;无独立 Pencil frame 的页面不漏测;Canvas Index 不计产品页 | +| `TASK-WP7-06` 追踪闭环与最终 AC 预冻结验收 | `TASK-WP7-01` 至 `TASK-WP7-05` | 运行 `test:all` 和 `validate:tdd-trace`;汇总候选制品、外部、UI/视觉/性能证据;用固定 super_admin/普通账号及正常链创建临时第二普通账号完成 52 AC;确认候选浏览器字段在验收期间未漂移 | `TDD-WP7-AC-001` | 52/52 当前 AC passed;109 需求 ID、13 模块、19 契约、9 错误、89/89 父族、117/117 case 完整;未来四项不计入;未通过时不得写最终 `RELEASE.json` | +| `TASK-WP7-07` 最终 RELEASE 冻结、制品生成与泄漏扫描 | `TASK-WP7-06` | 验收通过后把未漂移的候选浏览器记录写入最终 `RELEASE.json`,生成最终 ZIP、SHA-256 和 `START-HERE.txt`;把 RELEASE SHA-256 写入最终 `evidence.json`;扫描源码、Git、最终 ZIP、DB 配置、manifest、日志、截图、trace/evidence,并执行最终 package/support-gate smoke | `TDD-WP7-REL-001`、`TDD-WP7-SEC-001` | 最终记录、制品、Windows build、两个 full version、固定 major 和端口一致;零真实敏感值/用户绝对路径;最终写入或扫描失败时不得发布,且不能沿用候选 passed 状态冒充最终冻结 | + +任一真实外部门槛失败时,不得开始 P0-A 实际验收,也不得把 WP-7 或整体发布标记 passed。 + +## 5. 跨工作包集成任务 + +下列集成门禁不创建新的产品主责或测试族;由上游任务交付接口,下游任务消费并共同回归: + +| 集成门禁 | 上游 -> 下游 | 必须证明 | +| --- | --- | --- | +| `INT-01` 存储状态与项目 | WP-0 -> WP-2 | full 可纯 JSON 保存、unavailable 拒绝新增后端写;项目只保存已提交资源 ID;删除完成后物理重测 | +| `INT-02` 存储状态与导出 | WP-0 -> WP-2/WP-4 | 客户端下载与 latest 上传解耦;full/unavailable 不覆盖旧 latest;失败不撤回已下载文件 | +| `INT-03` 存储状态与素材 | WP-0 -> WP-5 | 只读归档不复制;managed/derived 受 5 GB 约束;原图/缩略图独立预留和计量 | +| `INT-04` 浏览器 gate | WP-0 -> WP-1 至 WP-7 | 全部产品 HTML、bundle 和 API 均在 gate 后;阻断页不读取身份、业务、资源或容量 | +| `INT-05` 安全配置应用 | WP-0 -> WP-1/WP-6 | API 原子应用后才生效;离线执行者无产品角色;失败无部分生效且有允许的 system 审计 | +| `INT-06` 任务诊断 | WP-2 -> WP-3/WP-6 | 未知上游结果进入成本核对,不新增用户任务状态、不补扣点数、不泄露供应商原文 | +| `INT-07` 模型三层状态 | WP-3 -> WP-2/WP-6 | configured default、runtime availability、recommended 独立;runtime 降级不改配置;推荐可空 | +| `INT-08` 私有告知 | WP-1 -> WP-6 | UserProfile 按身份+版本持久;区域服务端门禁;每次打开仍先写独立访问审计 | +| `INT-09` 贴纸引用与清理 | WP-2/WP-5 -> WP-6/WP-0 | release、active/trashed 项目和待发布引用阻止整批清理;排队不减容量;物理删除后重测 | +| `INT-10` 编辑器真实素材 | WP-5 -> WP-4/WP-7 | 编辑预览与导出使用同一声明式 renderer/版本/字体;白名单、替代项和 Chrome/Edge 视觉证据一致 | + +任一 `INT-*` 失败时,涉及的上下游任务均不能超过 Green;若失败来自真实环境或外部依赖,则保持 externally_blocked,不以局部 mock 结果覆盖。 + +## 6. 页面与状态交付归属 + +### 6.1 UIDesign coverage unit 归属 + +| coverage unit | 实现主责任务 | 最终证据任务 | +| --- | --- | --- | +| `support-gate` | `TASK-WP0-03` | `TASK-WP7-05` | +| `user-auth` | `TASK-WP1-02`、`TASK-WP1-03` | `TASK-WP7-03`、`TASK-WP7-05` | +| `workspace` | `TASK-WP2-01`、`TASK-WP2-05` | `TASK-WP7-05` | +| `current-task` | `TASK-WP2-05`、`TASK-WP2-06`、`TASK-WP3-04` | `TASK-WP7-05` | +| `projects` | `TASK-WP2-01`、`TASK-WP2-02`、`TASK-WP2-03` | `TASK-WP7-05` | +| `project-detail` | `TASK-WP2-01`、`TASK-WP2-03`、`TASK-WP2-07` | `TASK-WP7-05` | +| `editor` | `TASK-WP4-01`、`TASK-WP4-02`、`TASK-WP4-03`、`TASK-WP4-04`、`TASK-WP4-05`、`TASK-WP4-06`、`TASK-WP4-07` | `TASK-WP7-05` | +| `export` | `TASK-WP2-07`、`TASK-WP4-05` | `TASK-WP7-05` | +| `credits` | `TASK-WP2-04` | `TASK-WP7-05` | +| `settings` | `TASK-WP0-04`、`TASK-WP1-05` | `TASK-WP7-05` | +| `preview-user-variant` | `TASK-WP5-04`、`TASK-WP5-07` | `TASK-WP7-05` | +| `admin-auth` | `TASK-WP1-04` | `TASK-WP7-03`、`TASK-WP7-05` | +| `admin-overview` | `TASK-WP6-01`、`TASK-WP6-05` | `TASK-WP7-05` | +| `admin-users` | `TASK-WP6-01`、`TASK-WP2-04` | `TASK-WP7-05` | +| `admin-invites` | `TASK-WP6-01`、`TASK-WP1-01` | `TASK-WP7-05` | +| `admin-models` | `TASK-WP3-01`、`TASK-WP3-02`、`TASK-WP6-05` | `TASK-WP7-05` | +| `admin-assets` | `TASK-WP5-05`、`TASK-WP5-06`、`TASK-WP6-01` | `TASK-WP7-05` | +| `admin-preview` | `TASK-WP5-07`、`TASK-WP6-01` | `TASK-WP7-05` | +| `admin-generations` | `TASK-WP6-02` | `TASK-WP7-05` | +| `admin-services-storage` | `TASK-WP6-03`、`TASK-WP6-05` | `TASK-WP7-05` | +| `admin-audit` | `TASK-WP6-04` | `TASK-WP7-05` | +| `system-ui` | `TASK-WP0-07`、`TASK-WP0-08`、`TASK-WP0-09` | `TASK-WP7-05`、`TASK-WP7-07` | + +所有 coverage unit 的正常、空/加载、失败/禁用及适用的冲突/容量/特殊状态以 `tdd.md` 第 11.1 节为准。`UIDesign.md` 中没有独立低保真 frame 的页面仍必须实现和留证;预览 PNG 不得通过 OCR 反推固定文案。 + +### 6.2 Pencil 产品 frame 映射 + +下表覆盖 `.pen` 中全部 18 个产品 frame。状态范围只定位实现与证据,不替代 `UIDesign.md` 的完整状态矩阵;同一 frame 同时承载两个 coverage unit 时在 `page_id` 中并列。 + +| page_id | frame ID | 画板名 | 实现主任务 | 必须覆盖的状态范围 | +| --- | --- | --- | --- | --- | +| `user-auth` | `Z0pf8` | 01A 普通用户登录 | `TASK-WP1-02` | 登录正常;字段初始空;发码/登录加载;验证码、Resend 和身份状态失败;管理员入口、固定本机提示、艺术带与焦点 | +| `user-auth` | `DVPM8` | 01B 注册展开态 | `TASK-WP1-03` | 邀请与邮箱已验证摘要;资料和告知未完成/完成;注册中;名额、邀请、验证码和提交失败;主动作禁用 | +| `workspace` | `WbDYT` | 02A 空工作台 | `TASK-WP2-01` | 无项目艺术带;生成表单、推荐模型、四比例与空任务;模型/项目加载;模型、点数、参考图和容量禁用 | +| `workspace`、`current-task` | `d99Lo` | 02B 生成中工作台 | `TASK-WP2-05` | queued/running 无假百分比;冻结点数;critical 容量条;第二次提交禁用;失败/rejected 动作和配置变化重确认 | +| `projects` | `zXyG9` | 03A 项目列表 | `TASK-WP2-01` | active/trashed、筛选、多选 failed_empty;空/骨架;20 active、恢复/批量禁用;多标签和容量提示 | +| `project-detail` | `v2Qbu` | 03B 项目详情 | `TASK-WP2-01` | 历史、固定比例、继续生成、编辑、原图/latest;无成功图/无成品;10 图上限、资源失败和容量状态 | +| `editor` | `bHQnJ` | 04A 三栏编辑器 | `TASK-WP4-01` | 三栏、保存状态、画布、对象属性与固定底栏;画布/素材加载;保存/素材失败;第 51 元素;conflicted/full/unavailable | +| `export` | `i9upg7` | 04B 导出弹层 | `TASK-WP4-05` | JPG/PNG、质量、尺寸;合成/持久化加载;未提交确认;四类失败结果;冲突一次下载和 full/unavailable | +| `credits` | `YlKzE` | 05A 点数明细 | `TASK-WP2-04` | available/reserved、流水和分页;无流水/加载;失败保留安全摘要;负 available 如实显示且无购买入口 | +| `settings` | `Ws5E0` | 05B 设置与本机数据 | `TASK-WP1-05` | 资料、本机数据、容量摘要和固定文案;加载/保存失败;full/unavailable;注销确认、处理中和返回登录 | +| `admin-overview` | `e4e2Kz` | 06A 后台总览 | `TASK-WP6-01` | 名额、任务、成本核对、服务/容量和审计摘要;骨架/空事件;聚合失败;三模型状态分列和待处理事项 | +| `admin-models` | `YVfVG` | 06B 模型配置 | `TASK-WP6-05` | 完整三模型集合、default/runtime/recommended;加载;空推荐;替代/priority 禁用;CAS 冲突和整套回滚 | +| `admin-assets` | `A28V3c` | 06C 素材与历史清理 | `TASK-WP5-06` | 上传/版本/候选;无上传/无候选;格式/编译/权限失败;full/unavailable;引用冲突、排队、物理删除和重计量 | +| `admin-generations` | `OnsiT` | 06D 生成记录与私有告知 | `TASK-WP6-02` | 默认隐藏正文;身份+版本告知;逐次打开;空记录/审计写入加载;428、审计失败、升版重阻断 | +| `admin-services-storage` | `L3lde2` | 06E 服务与存储 | `TASK-WP6-05` | Resend/高德/网关/Worker/LocalDataRoot;指标加载;paused/degraded/unavailable;恢复前置、清理排队和重计量 | +| `admin-audit` | `DlCpN` | 06F 私有内容访问审计 | `TASK-WP6-04` | 两类独立审计、游标和 180 天;空/加载;查询失败;不可编辑删除;无正文、图片、密钥和路径 | +| `support-gate` | `u1adni` | 07A 浏览器硬阻断 | `TASK-WP0-03` | 四种阻断 reason、支持品牌/major、最小诊断;bundle/API 无绕过;无业务空态、身份或容量读取 | +| `system-ui` | `awt96` | 07B Windows 辅助系统界面 | `TASK-WP0-07` | 托盘/启动中/ready;启动失败、端口占用、API/Worker degraded;脱敏诊断;full/unavailable 告警与受限动作 | + +`Z8p3I` 00 Canvas Index 是非产品索引,仅由 `TASK-WP7-05` 校验设计资产清单,不计产品页面、实现任务或完成率。低保真中的版本、账号、点数、容量和时间均为示例值,运行状态必须来自 fixture,最终浏览器版本必须来自候选记录和验收后冻结的 `RELEASE.json`。 + +### 6.3 UIDesign-only 页面映射 + +以下 5 个 coverage unit 没有独立 Pencil 产品 frame,但仍是完整实现与证据范围;不得因无画板而跳过。 + +| page_id | frame ID | 画板名 | 实现主任务 | 必须覆盖的状态范围 | +| --- | --- | --- | --- | --- | +| `preview-user-variant` | `UIDesign-only` | 无独立画板 | `TASK-WP5-07` | 有效授权、无授权/空批次、加载、撤销/过期阻断;不出现后台权限;全局容量条 | +| `admin-auth` | `UIDesign-only` | 无独立画板 | `TASK-WP1-04` | 白名单邮箱验证码、字段空/加载、非白名单、停用/移除、Resend 失败;独立 audience 与返回普通登录 | +| `admin-users` | `UIDesign-only` | 无独立画板 | `TASK-WP6-01` | 脱敏列表/详情、暂停恢复/调点、空/筛选空/骨架、原因缺失和状态禁用、负 available | +| `admin-invites` | `UIDesign-only` | 无独立画板 | `TASK-WP6-01` | 创建/停用/期限/次数/脱敏复制、空/骨架、名额满、过期/耗尽、请求失败和幂等/CAS 结果 | +| `admin-preview` | `UIDesign-only` | 无独立画板 | `TASK-WP5-07` | 批次、模板状态、授权/撤销/到期、空/骨架、非法转换/发布失败;不出现 P0-B 全量开放 | + +## 7. 追踪与完成门禁 + +### 7.1 机器可读任务记录 + +`tasks.manifest.json` 是本计划的逐任务执行记录,不是新的需求、架构或测试来源。每个任务对象必须包含:`task_id`、`work_package`、`dependencies`、`requirements`、`acceptance_criteria`、`development_plan_sections`、`modules`、`parent_families`、完整 `case_ids`、逐父族 `first_red`、`minimal_green`、`refactor_scope`、`validation_commands`、`evidence_directories`、`automation`、`release_gate` 和 `ui_scope`。 + +manifest 必须满足: + +- 52 个任务全部有独立记录;`TASK-WP7-01` 是不新增父族/case 的候选发布支撑任务,其余任务按本计划拥有父测试族; +- 117 个具体 case_id 恰好各出现一次并映射到其已知父族,不只依赖运行时模糊匹配; +- requirements、AC、首个 Red、最小 Green 和层级从当前 `tdd.md` 的父族/case 定义确定性取得,不能由任务实现者自行补写; +- DevelopmentPlan 章节、模块边界、Refactor 范围和 UI scope 由本计划固定;模块路径尚未在上游冻结时只使用已冻结组件名,不擅自固化目录结构; +- `tasks.md`、`tasks.manifest.json` 或任一冻结输入变化时,追踪校验先比较哈希与集合,再决定受影响任务状态,不能只更新 manifest 哈希。 + +### 7.2 数量闭环 + +| WP | 开发任务 | 本计划唯一归属父测试族 | `tdd.md` 规范 case | 当前 AC 唯一主责 | +| --- | ---: | ---: | ---: | ---: | +| WP-0 | 9 | 17 | 20 | 4 | +| WP-1 | 6 | 11 | 13 | 7 | +| WP-2 | 7 | 15 | 30 | 16 | +| WP-3 | 4 | 7 | 10 | 2 | +| WP-4 | 7 | 15 | 17 | 13 | +| WP-5 | 7 | 9 | 9 | 5 | +| WP-6 | 5 | 8 | 11 | 4 | +| WP-7 | 7 | 7 | 7 | 1 | +| 合计 | 52 | 89 | 117 | 52 | + +`pnpm validate:tdd-trace` 必须从结构化来源或确定性解析验证: + +- 本计划列出的父测试族集合与 `tdd.md` 第 6.1 至 6.8 节完全相等,恰为 89 个,且每个只归属一个开发任务; +- 第 6.9 节 117 个 case 都能按稳定正则映射到已知父族,89/89 父族至少一个 case; +- `tasks.manifest.json` 中 52 个任务齐全,117 个 case_id 各出现一次,任务的 derived automation/release_gate 与父族层级一致; +- 52 个当前 AC 恰有一个 WP 主责,AC-08、AC-26、AC-37、AC-54 不得进入当前 passed 统计; +- 109 个当前 PRD 需求 ID、十三个功能模块、十九个数据契约、九类 GenerationErrorCategory 和 22 个 UI coverage unit 均有落点; +- `.pen` 的 18 个产品 frame ID 各有 page_id、主任务和状态范围;5 个无独立画板 coverage unit 明确标为 `UIDesign-only`; +- 每个 case 的必填字段、派生 `automation`、`release_gate`、逐层状态和证据目录非空且未被人工改写。 + +### 7.3 工作包完成检查 + +| WP | 自动门禁 | 真实/人工门禁 | 不能提前宣告 | +| --- | --- | --- | --- | +| WP-0 | 单元、API、存储、Supervisor、安全、打包 Green | 最终浏览器记录在 WP-7 | 固定依赖、回环、容量等号、日志/诊断/凭据、无备份不迁移任一未过 | +| WP-1 | 认证状态机、事务、会话、名额、注销、审计 Green | Resend 正式认证 | 预置账号或 mock 邮件不得替代真实认证 | +| WP-2 | 项目、点数、任务、参考图、冲突、错误 Green | 真实模型完成态依赖 WP-3 | mock 成功不得替代真实模型结算 | +| WP-3 | 配置集合、runtime、Adapter mock、Worker 恢复 Green | 三模型独立真实契约 | 任一模型未匹配最后配置完成全矩阵 | +| WP-4 | 编辑器、导出、a11y、性能 harness Green | 最终视觉依赖 WP-5 真实白名单 | 占位字体/资源或单浏览器通过 | +| WP-5 | 编译、目录、隔离、缓存、上传、清理 Green | 真实归档只读校验与人工视觉 | 自动清理、引用未清、排队即减量或逐批公开 full_p0 | +| WP-6 | 后台、告知、审计、服务、存储 Green | 真实服务恢复与正式身份流程 | 浏览器状态代替后端确认、逐次审计缺失、敏感信息回显 | +| WP-7 | 候选记录、`test:all`、追踪和预冻结 AC Green | 精确 full version UI/视觉/性能、AI、Resend、高德通过后才写最终 RELEASE;最终制品扫描通过 | 候选记录被当作最终 RELEASE,或任一外部门槛、页面状态、AC、最终哈希/扫描未完成 | + +### 7.4 P0-A 最终完成定义 + +只有同时满足以下条件,开发任务计划才完成: + +1. WP-0 至 WP-7 全部达到各自门禁,所有任务为 `passed`,没有 `source_changed`、`red`、`green` 或 `externally_blocked` 遗留。 +2. 89/89 父测试族、117/117 规范 case 及参数分支均有可复现 Red、最终 Green/Passed 和脱敏证据。 +3. 52/52 当前 AC passed,109 个需求 ID、十三个模块、十九个契约和九类错误全部闭环;`reference_invalid` 与 `safety_rejected` 证据分离。 +4. 存储精确阈值、等号写入、超限零副作用、原图/缩略图计量、full/unavailable 动作和物理删除后重测全部通过。 +5. ModelConfig 始终恰好一个 enabled default、priority 唯一正整数;runtime 不回写配置;recommended 可为空。 +6. 私有告知按 super_admin 身份+版本持久,升版重阻断;每次私有图片/完整提示词打开仍先写独立审计。 +7. 浏览器实际枚举确认 SW、Cache Storage 和 IndexedDB 不含 internal/private、项目、提示词或未保存草稿。 +8. 临时第二普通账号通过正常邀请/验证码链创建,完成跨用户隔离后正常注销并进入规定清理路径。 +9. 22 个 UI coverage unit 的全部适用状态在最终 Chrome/Edge、1920×1080、100%/200% 下无不可用重叠,焦点、文案、视觉、50 元素和四比例导出通过。 +10. 三模型、Resend、高德真实门槛通过;没有自动充值、收费备用、自动恢复或自动扩容。 +11. 最终 ZIP、SHA-256、`RELEASE.json`、`START-HERE.txt`、固定端口和真实浏览器记录一致,局域网、其他设备及不支持浏览器不能进入产品。 +12. 代码、制品、日志、配置、manifest、截图、trace 和证据零真实凭据、验证码、会话令牌、管理员真实邮箱、完整私有内容和硬编码用户绝对路径。 +13. 普通用户主框架和设置区持续原样显示:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +## 8. 当前计划明确不包含 + +- 不创建 AC-08、AC-26、AC-37、AC-54 或任一 P0-B 当前开发任务。 +- 不实现手机、局域网、远程访问、跨设备、公司系统合并、对象存储、产品 HTTPS、云备份、项目归档、数据迁移或恢复。 +- 不实现 P1 的本地底图上传、空白画布、普通贴纸关键词搜索、公开注册准备、高德主体迁移或正式合规条款。 +- 不实现支付、订阅、自动充值、批量生成/导出、复制生成、社区、普通文字、用户自定义贴纸、AI 贴纸、GIF/视频、直发小红书、局部编辑、协作、版本历史、编组或图层面板。 +- 不重新选择技术栈,不引入 Docker、Redis、远程数据库/队列、对象存储、外部日志/指标、自动更新或自动扩容。 +- 不在本计划中虚构人员、日历工期或故事点。排期只有在团队规模、可用工时、外部凭据窗口和真实设备排期确定后,基于本任务依赖图另行制定。 diff --git a/tdd.md b/tdd.md new file mode 100644 index 0000000..7854ef8 --- /dev/null +++ b/tdd.md @@ -0,0 +1,1017 @@ +# Dada P0-A 测试驱动开发计划 + +| 文档项 | 内容 | +| --- | --- | +| 文档版本 | 1.4 | +| 编写日期 | 2026-07-27 | +| 适用产品版本 | PRD v1.10,P0-A 本机测试需求冻结稿 | +| 功能索引版本 | FeatureSummary 1.1 | +| 工程计划版本 | DevelopmentPlan 1.1 | +| UI 设计版本 | UIDesign 1.0 | +| 当前交付 | 仅 P0-A Windows 本机测试版 | +| 产品需求唯一权威 | `PRD.md` | +| 文档职责 | 固定测试驱动节奏、测试层级、fixture、工作包门禁、AC 追踪、证据和发布验收方法 | +| 当前状态 | 可用于开发的候选测试基线;`RELEASE.json` 尚未生成,不能据此宣告发布验收完成 | + +## 1. 文档定位与冻结输入 + +### 1.1 权威顺序 + +1. `PRD.md` v1.10 是产品范围、状态、权限、业务规则、数据语义和验收结果的唯一权威。 +2. `FeatureSummary.md` 1.1 只用于定位十三个功能模块、关键状态、十九个数据契约和 AC,不产生测试新需求。 +3. `DevelopmentPlan.md` 1.1 固定技术栈、OpenAPI、SQLite、事务、存储、进程、工作包及测试层级;本文件不得改写这些工程决策。 +4. `UIDesign.md` 1.0 与低保真资产用于页面、控件、状态、文案、焦点、布局和界面验收;它们不得覆盖 PRD 行为或 DevelopmentPlan 接口。 +5. 本文件只定义如何先写失败测试、如何得到最小通过实现、如何回归和留证;不修改产品 API、Schema、状态、稳定错误码或 `message_key`。 + +若本文件与上游冲突,停止受影响测试和实现,以 PRD 为产品权威重新判断,并按依赖顺序同步 DevelopmentPlan、UIDesign 和本文件。不得只替换哈希后沿用旧结论。 + +### 1.2 输入哈希与失效作用域 + +#### 1.2.1 全局冻结来源 + +| 输入 | SHA-256 | +| --- | --- | +| `PRD.md` | `31F93674DF1A90B557FEE3AA9E74FB084E246CA8FE09F6BD4B1DC9D56D606565` | +| `FeatureSummary.md` | `6F80E272AAB08A5525B54501D83F16A4F6A7A170596947BBC25F1DA54F2FE844` | +| `DevelopmentPlan.md` | `7C579E9DF8D00DA9333F587D05FC966123437EE68575D87D73C903C1487FA9D6` | +| `UIDesign.md` | `0C449DF833E5471843E17EB74F5B470EBC955F117CAF1D79AC9C0EF90DE763F0` | + +上述四份 Markdown 是所有测试层的全局来源基线。每次测试运行先校验四项哈希;任一不一致时: + +1. 整次运行标记为 `source_changed`,不得继续生成通过证据。 +2. 完整重读发生变化的权威来源及全部受影响下游来源。 +3. 更新需求、UI、fixture、测试和 AC 映射后重新建立失败基线。 +4. 不把变更前的截图、性能数据、外部契约记录或人工确认复用为变更后的通过证据。 + +#### 1.2.2 UI 参考资产 + +| 输入 | SHA-256 | +| --- | --- | +| `Dada-P0A-LowFi.pen` | `A352C8F205E9399EB2E6306176253AF6FF65DDDBB9FDCEA2E2FE13E3C3459975` | + +低保真预览图清单: + +| 文件 | SHA-256 | +| --- | --- | +| `Dada-LowFi-Preview/awt96.png` | `9E0392DBEA2407BC0602E62EDEE755DF85BE085B0A6CD328C69DA8C79832AB2C` | +| `Dada-LowFi-Preview/bHQnJ.png` | `FA9F1CFC664F90BB8CD5DCFE985DA9A1280B3F3EF3C971F9480CB226A6D6DC88` | +| `Dada-LowFi-Preview/d99Lo.png` | `2ADC0DE944290E2BDFFF91083F0A3AB5BBEB0C3E294E0DC39CAE55763EFCF0C0` | +| `Dada-LowFi-Preview/DVPM8.png` | `9B1E7237857089CB3185EC4D3BBC2B3C9293267D7026A4F411A2131175FDEE90` | +| `Dada-LowFi-Preview/e4e2Kz.png` | `9D528A72A4902934B77421FB53EF35457B62AEA0477155BA8664B38DF0833232` | +| `Dada-LowFi-Preview/WbDYT.png` | `703A3D371F716C808CA158700D380B7CAFA25D89A7672B7199362A8A65E8EB76` | +| `Dada-LowFi-Preview/Z0pf8.png` | `ED213DFD7022BC355C7E243CD27484BFB25791DF2C38ED1A498175C563E17B9B` | +| `Dada-LowFi-Preview/Z8p3I.png` | `4714CA2C6D4CC00395B81B175389CF04DDE9EB0C71876BDBE72060DC741A1035` | + +`.pen` 和预览 PNG 不是产品需求来源,只约束第 11 节映射到的 UI、视觉和 E2E 证据。相关 job 运行前校验这些哈希;某张 PNG 变化时只把对应页面/状态的 UI、视觉和 E2E case 标记为 `source_changed`,`.pen` 变化时先比较受影响 frame,只失效对应页面证据;若无法确定受影响 frame,则失效全部 UI、视觉和 E2E 证据。UNIT、DB、API、WRK、EXT-MOCK、EXT-REAL 和不消费布局的 PKG-SEC 证据不得仅因低保真资产变化而失效。所有失效项都必须重新读取 UIDesign 与变更资产、更新映射并重建视觉基线,不能只替换哈希。 + +### 1.3 当前范围 + +P0-A 只在项目方当前 Windows 电脑上运行。Fastify 只监听 `127.0.0.1:43121`,浏览器只从 localhost 访问本地后端,业务数据只写入 `LocalDataRoot`,约 3.57 GB 规范素材从安全配置引用的原目录只读使用。本地后端访问 AI 网关、Resend 和高德时仍使用外网 HTTPS。 + +当前测试不得实现或暗示手机、局域网、远程访问、跨设备继续处理、公司服务器、对象存储、正式域名、产品 HTTPS、云备份、项目归档或 P0-A 数据迁移。AC-08、AC-26、AC-37、AC-54 是未来 P0-B 门槛,不进入当前完成统计;AC-15、AC-23、AC-36、AC-38 只执行 PRD 明确的 P0-A 子集。 + +当前 AC 总数固定为 52: + +- P0-A 必验 48 项:AC-01 至 AC-07、AC-09 至 AC-14、AC-16 至 AC-22、AC-24 至 AC-25、AC-27 至 AC-35、AC-39 至 AC-53、AC-55 至 AC-56。 +- P0-A 子集 4 项:AC-15、AC-23、AC-36、AC-38。 +- 未来且不计入当前完成 4 项:AC-08、AC-26、AC-37、AC-54。 + +## 2. TDD 执行协议 + +### 2.1 Red-Green-Refactor + +每个能力和缺陷都按以下顺序执行,不能先实现后补测试: + +1. **Red**:先写一个只因目标行为尚未实现而失败的最小测试。记录测试 ID、权威需求、fixture、预期失败原因和所有禁止副作用。 +2. **确认 Red**:在隔离环境运行该测试,确认失败信息指向目标缺口,而不是环境、随机数据、未安装依赖或其他已知失败。 +3. **Green**:只实现使该测试通过的最小产品行为;不得借机增加 PRD 未定义的入口、状态、回退或配置。 +4. **回归 Green**:运行同层测试、所属工作包测试和跨工作包依赖测试。金融、文件、权限、审计和外部调用必须同时断言状态与无副作用。 +5. **Refactor**:在测试持续通过的前提下消除重复、改善结构;不得改变 OpenAPI、Schema、事务边界和用户行为。 +6. **Evidence**:保存脱敏证据清单。没有 Red 证据、最终 Green 结果或必要人工证据的场景不能标记完成。 + +测试状态只允许: + +| 状态 | 含义 | +| --- | --- | +| `not_started` | 尚未建立失败测试 | +| `red` | 已确认测试因目标缺口失败 | +| `green` | 自动断言通过,但尚可能等待工作包或发布门禁 | +| `externally_blocked` | 本地模拟通过,但等待真实 AI 网关、Resend、高德、浏览器或发布环境证据 | +| `passed` | 自动、人工、外部及证据门槛全部满足 | +| `source_changed` | 全局来源或作用域内 UI 参考变化,受影响结果按第 1.2 节失效 | + +不设置 `skipped_as_passed`、`expected_failure_as_passed` 或无依据豁免。临时跳过只能保持未完成并写明原因。 + +### 2.2 测试 ID 与用例记录 + +测试族 ID 使用 `TDD-WP<0-7>--`,例如 `TDD-WP0-STO-002`。一个测试族可以包含参数化子用例,子用例追加稳定后缀,例如 `TDD-WP0-STO-002-equal-limit`。 + +每个可执行用例必须记录: + +| 字段 | 要求 | +| --- | --- | +| `test_id` | 全局唯一且提交后不复用 | +| `work_package` | 唯一主责 WP;交叉依赖另列 supporting WP | +| `requirements` | 一个或多个 PRD 需求 ID、数据契约或 NFR | +| `acceptance_criteria` | AC 编号及 P0-A 子集标记 | +| `layer` | 本文第 3 节固定层级 | +| `fixture_ids` | 本文第 5 节稳定 fixture | +| `red_reason` | 实现前预期失败的唯一原因 | +| `green_assertions` | 状态、响应、持久化、UI 和禁止副作用 | +| `automation` | 按第 2.2.1 节生成的非空有序数组,元素只允许 `automated`、`controlled_real`、`manual_review` | +| `evidence_refs` | 机器可读结果、截图、性能、日志或人工记录 | +| `release_gate` | 按第 2.2.1 节生成的非空门禁数组,明确阻止的工作包和 P0-A 发布 | + +#### 2.2.1 测试族执行属性 + +本文实际定义 89 个测试族;AREA 允许大写字母和数字,因此 `TDD-WP4-A11Y-001` 必须计入。`pnpm validate:tdd-trace` 生成 `artifacts/tdd//family-manifest.json`,逐族按下列规则确定执行属性,不能依赖人工补默认值: + +| 字段 | 确定规则 | +| --- | --- | +| `red_reason` | 精确继承父测试族“首个 Red”;case 可追加更窄触发条件,但不得删除或弱化父断言 | +| `automation` | 非空有序数组:父层级含 UNIT/DB/API/WRK/EXT-MOCK/E2E/PKG-SEC/VIS-PERF 时含 `automated`;含 EXT-REAL 时含 `controlled_real`;含 MANUAL 时含 `manual_review`;固定按此顺序去重 | +| `release_gate` | WP-0 至 WP-6 固定为 `["work_package:WP-n","release:P0-A"]`;WP-7 固定为 `["release:P0-A"]` | +| `work_package` | 从测试族 ID 的 `WP` 唯一解析;case 不得覆盖 | +| `layer` | 精确继承父测试族“层级”列;case 结果必须逐层列出执行状态,不得以 E2E 代替 DB/API 或以 mock 代替真实门禁 | + +因此第 2.2 节 `automation` 与 `release_gate` 在 `result.json` 中使用上述数组值。任一父族缺“首个 Red”或“层级”、属性数组为空、case 指向未知父族或人工改写派生值时,追踪校验失败。 + +### 2.3 测试脚本接口 + +后续工程必须提供以下仓库级脚本;脚本名称属于开发接口,不是产品 API: + +| 命令 | 作用 | +| --- | --- | +| `pnpm test:unit` | TypeBox、纯函数、状态转换和前端域控制器 | +| `pnpm test:integration` | SQLite、临时 LocalDataRoot、文件一致性和服务事务 | +| `pnpm test:api` | Fastify inject、OpenAPI、鉴权、错误信封和资源路由 | +| `pnpm test:worker` | 队列、租约、崩溃、结算、清理和重计量 | +| `pnpm test:e2e` | Playwright Chrome/Edge 独立 profile 的产品流程 | +| `pnpm test:visual` | 固定 Canvas 与页面截图、Chrome/Edge 差异和人工复核包 | +| `pnpm test:performance` | 50 元素、自动保存、打开编辑器和导出预算 | +| `pnpm test:security` | 凭据、日志、制品、路径、缓存和私有内容泄漏扫描 | +| `pnpm test:package` | 便携包、随包 Node/native 模块、Supervisor 和固定端口边界 | +| `pnpm validate:tdd-trace` | 校验当前 AC 唯一主责、P0-B 排除项、PRD 需求 ID、UIDesign 页面/状态覆盖和 case 必填字段,防止追踪矩阵手工漂移 | +| `pnpm test:all` | 除真实外部调用外的全部阻断测试 | +| `pnpm validate:external -- --service [--model ]` | 显式受控真实验证;普通 CI 禁止运行 | + +任一脚本失败必须返回非零退出码。测试脚本不得读取真实 P0-A `LocalDataRoot`、日常浏览器 profile 或未显式授权的 Windows 凭据。 + +### 2.4 证据清单 + +每次运行写入 CI 或验收工作目录下的 `artifacts/tdd//evidence.json`。该目录不是产品数据目录,不提交真实私有内容。清单至少包含: + +```json +{ + "schema_version": 1, + "run_id": "opaque-run-id", + "commit": "git-commit", + "source_hashes": {}, + "ui_reference_hashes": {}, + "environment": {}, + "release_record_hash": null, + "tests": [ + { + "test_id": "TDD-WP0-BRW-001-real-supported", + "parent_family": "TDD-WP0-BRW-001", + "work_package": "WP-0", + "requirements": ["NFR-01"], + "acceptance_criteria": ["AC-24"], + "layer": ["E2E", "MANUAL"], + "fixture_ids": ["final-release-browser-fixture"], + "red_reason": "真实支持 Chrome/Edge 无法通过门禁", + "green_assertions": ["release-recorded full version passes"], + "automation": ["automated", "manual_review"], + "release_gate": ["work_package:WP-0", "release:P0-A"], + "status": "green", + "started_at": "ISO-8601", + "finished_at": "ISO-8601", + "evidence_refs": [] + } + ], + "redaction_scan": "passed" +} +``` + +`evidence.json`、截图、trace、日志、OpenAPI 示例和 fixture 均不得包含真实密钥、验证码、会话令牌、管理员真实邮箱、完整私有提示词、真实用户图片、Windows 用户名、绝对本机路径或供应商原始错误正文。扫描失败时整次运行失败。 + +## 3. 固定测试层级 + +| 层级 | 标识 | 基础设施 | 主要职责 | +| --- | --- | --- | --- | +| Schema/单元 | `UNIT` | Vitest、TypeBox fixture、可控时钟 | 枚举、Schema、格式化、版本、状态转换、路径、容量计算、色卡确定性 | +| SQLite/文件集成 | `DB` | 每测试临时 SQLite、临时 LocalDataRoot、受控文件驱动 | 事务、索引、幂等、审计、引用、预留、物理清理和恢复 | +| API 契约 | `API` | Fastify inject、OpenAPI snapshot、原始 HTTP 客户端 | 路由、错误信封、门禁、鉴权、CSRF、If-Match、multipart、资源隔离 | +| Worker 恢复 | `WRK` | 模拟网关、进程终止 harness、可控租约时钟 | 领取、心跳、恢复、结算一次、成本核对、清理重放 | +| 外部模拟 | `EXT-MOCK` | 三模型、Resend、高德的确定 fixture | 错误、配额、同步/异步和恢复状态;普通 CI 不访问外网 | +| 外部真实 | `EXT-REAL` | 显式命令、Windows 凭据管理器、受控账号 | 三模型独立契约、邮件送达、高德接口和发布门槛 | +| 浏览器流程 | `E2E` | Playwright Chrome/Edge channel、独立 user-data-dir | 认证、项目、编辑、导出、多标签、后台、缓存、权限和门禁 | +| 安全/打包 | `PKG-SEC` | .NET/Node harness、进程检查、ZIP/日志/环境扫描 | 凭据传递、固定端口、便携包、诊断脱敏和零泄漏 | +| 视觉/性能 | `VIS-PERF` | 固定 Canvas、截图 diff、PerformanceObserver | NFR-02、NFR-03、1080p、200% 缩放、50 元素和导出 | +| 人工发布 | `MANUAL` | 真实便携包、真实外部依赖、固定验收账号 | 自动化不能替代的视觉、送达、替代资源和发布确认 | + +低层测试发现的数据、状态和权限错误不能只由 E2E 覆盖;E2E 负责证明用户流程接线正确。真实外部测试不能替代模拟失败分支,模拟测试也不能替代真实发布门槛。 + +### 3.1 OpenAPI 与事件分组覆盖 + +| 分组 | 主测试族 | 必验契约 | +| --- | --- | --- | +| 浏览器支持 | `TDD-WP0-BRW-001`、`TDD-WP0-BRW-002` | support check、426、最小字段、产品路由前置门禁 | +| Bootstrap | `TDD-WP0-API-001`、`TDD-WP3-MDL-003` | app、公开功能、模型配置/runtime/推荐和非敏感依赖摘要 | +| 普通认证 | `TDD-WP1-AUTH-*` | 注册/登录分离、挑战、会话、限流和稳定错误 | +| 管理员认证 | `TDD-WP1-ADM-001`、`TDD-WP1-CFG-001` | 白名单前置校验、独立 audience、告知版本摘要 | +| 账号 | `TDD-WP1-DEL-001`、`TDD-WP2-CRED-*` | 资料、点数、流水和不可逆注销 | +| 模型 | `TDD-WP3-MDL-*` | 配置集合、版本、runtime availability 和派生推荐 | +| 生成 | `TDD-WP2-GEN-*`、`TDD-WP2-REF-001` | multipart、配置复核、单任务、参考图和任务真相 | +| 项目 | `TDD-WP2-PROJ-*` | 所有者、If-Match、完整 JSON、回收站和 purge | +| 历史与成品 | `TDD-WP2-EXP-*`、`TDD-WP4-EXP-001` | 原图、历史、每格式 latest 与客户端下载分离 | +| 三类素材 | `TDD-WP5-RES-001`、`TDD-WP5-PREV-001` | public/preview/private 分路由、鉴权和缓存 | +| 贴纸历史清理 | `TDD-WP5-CLN-001` | 候选、intent、确认、整批复核、异步结果 | +| 私有告知与访问 | `TDD-WP6-PRIV-*` | 身份+版本 ack、服务端区域门禁、先审计后读取 | +| 管理后台 | `TDD-WP6-ADM-001`、`TDD-WP6-AUD-001` | active super_admin、事务内审计和安全摘要 | +| 状态事件 | `TDD-WP0-EVT-001` | 仅提示 ID/类型/版本/时间;断线或缺号后 REST refetch | + +所有 `/api/v1` JSON、multipart、SSE 和错误响应都由 TypeBox/OpenAPI 3.1 的同一 Schema 来源验证。前端生成客户端 snapshot 漂移、未声明枚举、错误 HTTP 状态或错误信封字段时,API 契约测试失败。 + +## 4. 候选环境与发布冻结 + +### 4.1 当前候选基线 + +| 项目 | 候选值 | 用途 | +| --- | --- | --- | +| 操作系统 | Microsoft Windows 11 家庭版 中文版,25H2,`10.0.26200`,build `26200` | 开发视觉、性能、打包和辅助界面候选基线 | +| Chrome | `150.0.7871.184`,major `150` | 当前真实 Chrome channel 候选基线 | +| Edge | `150.0.4078.99`,major `150` | 当前真实 Edge channel 候选基线 | +| 产品视口 | `1920 x 1080` CSS px | UIDesign 首要桌面验收视口 | +| 截图 DPR | `1` | Playwright 确定性页面与 Canvas 基线 | +| 默认缩放 | `100%` | 正常布局、视觉和性能 | +| 可访问性缩放 | `200%` | 文字覆盖、弹层、主动作和滚动可达性 | +| 语言 | `zh-CN` | 固定简体中文文案和排序 | +| 时区 | `Asia/Shanghai` | 界面时间显示;持久化仍为 UTC 毫秒 | + +上述浏览器必须使用本机真实 Chrome/Edge channel。Playwright 自带 Chromium 只能用于开发诊断,不能作为 AC-24、AC-32 或发布通过证据。伪造 User-Agent 或 UA-CH 可以证明拒绝分支,但不能证明支持分支。 + +本节具体版本号只用于当前候选视觉、性能和打包基线,不是产品支持范围本身,也不得写死进产品逻辑;产品运行支持口径以最终 `RELEASE.json` 和浏览器门禁实现为准。 + +### 4.2 `RELEASE.json` 缺失门禁 + +当前目录没有 `RELEASE.json`,因此本节环境只能标为 candidate。WP-7 必须: + +1. 从实际发布包记录 Windows build、Chrome/Edge 品牌、major、full version 和记录时间。 +2. 在记录的两个真实 full version 上重新运行 AC-24、AC-32、全部 E2E、视觉、性能、下载、文件选择和 200% 缩放。 +3. 对比本节候选值。任何不同都使候选截图和性能结果失去发布证据资格,但不自动否定与浏览器无关的单元/DB 测试。 +4. 把 `RELEASE.json` SHA-256 写入最终 `evidence.json`;缺失、版本不一致或哈希不一致时 WP-7 失败。 +5. 运行时只接受 `RELEASE.json` 记录的 major,不联网查询“当前版本”,也不接受前一 major。 + +## 5. 固定 fixture + +### 5.1 主体与认证 + +全部自动化邮箱使用 `.invalid`,不得使用项目方真实邮箱。 + +| fixture | 内容 | +| --- | --- | +| `FX-ACTOR-USER-A` | active 普通用户,10 available、0 reserved,拥有项目 A | +| `FX-ACTOR-USER-B` | 通过正常邀请、验证码和注册链临时创建的第二普通用户 | +| `FX-ACTOR-SUSPENDED` | suspended 普通用户,仍占普通账号名额 | +| `FX-ACTOR-DELETED` | 已注销主体,只保留允许的匿名事件 | +| `FX-ACTOR-ADMIN-A` | active super_admin,已在安全白名单 | +| `FX-ACTOR-ADMIN-B` | 第二个独立 active super_admin | +| `FX-ACTOR-DEPLOYER` | 无 UserProfile、无会话、无产品角色的配置维护主体 | +| `FX-INVITE-VALID` | 未过期、启用且有剩余次数 | +| `FX-INVITE-EXPIRED` | 已过期 | +| `FX-INVITE-DISABLED` | 已停用 | +| `FX-INVITE-EXHAUSTED` | 已达最大使用次数 | +| `FX-CHALLENGE` | 10 分钟一次性验证码、60 秒重发限制、带 pepper 摘要 | + +跨普通用户隔离的发布验收不得直接插入 `FX-ACTOR-USER-B`。它必须在独立 Chrome/Edge profile 中使用真实可验收的邀请和验证码链创建,完成越权检查后正常注销,并等待内容进入规定清理路径。直接 DB fixture 只用于低层事务测试。 + +### 5.2 模型、任务与点数 + +| fixture | 内容 | +| --- | --- | +| `FX-MODEL-SEED` | 三模型 configured enabled;Flash 唯一默认 priority 1,Pro priority 2,GPT priority 3;初始 contract unverified、runtime unavailable | +| `FX-MODEL-VERIFIED` | 三模型分别具有独立 verified 证据和可用 runtime 状态 | +| `FX-MODEL-NO-RUNTIME` | 配置不变,三个模型 runtime 均不可用于新任务 | +| `FX-MODEL-CONFLICT` | 过期 config_set_version、非法默认替代或重复优先级候选集合 | +| `FX-CREDIT-ENOUGH` | available 10、reserved 0、模型成本 1 | +| `FX-CREDIT-INSUFFICIENT` | available 0、reserved 0、模型成本 1 | +| `FX-CREDIT-NEGATIVE` | 仅由 admin_adjustment 产生 available -5,reserved 保持不变 | +| `FX-JOB-ACTIVE` | 一个 queued 或 running 任务及一份 generation_reserve | +| `FX-JOB-TERMINAL` | succeeded、failed、rejected 的不可逆终态组合 | +| `FX-GATEWAY-SYNC` | 同步完成模型响应 | +| `FX-GATEWAY-ASYNC` | start 返回上游引用、poll 返回终态 | +| `FX-GATEWAY-UNKNOWN` | 已提交但无可安全查询引用;`GenerationJob.status=failed`,`error_category=unknown_retryable`,`upstream_cost_reconciliation=pending_manual_review`,`upstream_outcome_known=false`,冻结点释放一次 | + +### 5.3 项目、参考图与画布 + +| fixture | 内容 | +| --- | --- | +| `FX-PROJECT-EMPTY` | failed_empty 草稿,无成功图 | +| `FX-PROJECT-HISTORY-9` | 9 张成功图,可继续生成 | +| `FX-PROJECT-HISTORY-10` | 10 张成功图,阻止追加 | +| `FX-PROJECT-ACTIVE-19` | 19 个 active 项目,可再创建一个 | +| `FX-PROJECT-ACTIVE-20` | 20 个 active 项目,阻止创建和恢复 | +| `FX-PROJECT-CONFLICT` | 两个标签基于同一 state_version 的不同内存状态 | +| `FX-REF-VALID` | 可解码的 JPG、PNG、WebP,分别满足模型限制 | +| `FX-REF-MALFORMED` | 扩展名与内容不符、损坏解码和错误 MIME | +| `FX-REF-OVERSIZE` | 超单文件、总大小或数量限制 | +| `FX-REF-NONSAFETY-INVALID` | 供应商明确判定不可用但不属于安全拒绝的参考图 | +| `FX-REF-SAFETY-REJECTED` | 供应商明确安全拒绝证据;不得与前一 fixture 共用模拟错误 | +| `FX-CANVAS-50` | 12 个文字、24 个普通贴纸、4 个色卡、10 个动态贴纸,共 50 个覆盖元素 | +| `FX-CANVAS-RATIOS` | 3:4、1:1、4:3、9:16 四个固定画布和标准像素 | + +### 5.4 时间、位置与视觉 + +- 固定后端时钟为 `2026-07-27T04:00:00.000Z`,界面期望为 Asia/Shanghai 的 `2026-07-27 12:00:00`。 +- DYN004 使用确定测试坐标与固定地理编码响应;拒绝 Dada 自有提示时定位 API 调用次数必须为 0。 +- 色卡背景使用固定解码像素和 `mmcq-v1`;测试裁剪、调色、滤镜不改变五色,更换原始底图会改变五色。 +- 字体测试等待 `FontFace` ready;字体缺失使模板 unavailable。DYN012 只使用 FONT081 Lexend Deca,并始终标记替代字体。 +- 动态值、动画时刻、随机 ID 生成器、列表排序和图片解码均固定 seed;视觉基线不得依赖当前墙钟或网络返回顺序。 + +### 5.5 存储边界 + +容量测试通过受控文件驱动、数据库计数和预留模拟精确字节,不实际创建 5 GB 文件。必须参数化以下值: + +| 字节值 | 期望 | +| ---: | --- | +| `4,294,967,295` | normal | +| `4,294,967,296` | warning 起点 | +| `4,831,838,207` | warning 末值 | +| `4,831,838,208` | critical 起点 | +| `5,368,709,119` | critical 末值 | +| `5,368,709,120` | full;若由本次写入恰好达到,写入成功后进入 full | +| `5,368,709,121` | 超限请求拒绝;不得创建持久副作用 | + +计量分类至少区分 reference、generated、export、derived、`sticker_original`、`sticker_thumbnail`。只读规范素材、应用代码、数据库、日志、审计、浏览器公开缓存和下载目录均不得计入。 + +## 6. 工作包 TDD 门禁 + +### 6.1 WP-0 基础工程与本机边界 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP0-DEP-001` | 冻结依赖任一无法安装、加载或最小运行 | 精确版本、TypeScript、React/Fabric、Fastify、better-sqlite3 native 和随包 Node 全通过;不私自替换技术栈 | UNIT、PKG-SEC | +| `TDD-WP0-API-001` | TypeBox、OpenAPI、生成客户端或错误信封漂移 | OpenAPI 3.1 snapshot 与运行验证一致;响应有 correlation ID;`details` 只含白名单字段且不泄露内部信息 | UNIT、API | +| `TDD-WP0-BND-001` | API 接受非回环绑定、错误 Host/Origin 或 CORS | 只绑定 `127.0.0.1:43121`;拒绝局域网、其他 Host/Origin,不创建防火墙入站规则 | API、PKG-SEC | +| `TDD-WP0-BRW-001` | 真实支持 Chrome/Edge 无法通过门禁 | `RELEASE.json` 记录的两个真实 full version 通过并建立短期支持 Cookie,产品 API 每次复核品牌/major | E2E、MANUAL | +| `TDD-WP0-BRW-002` | 其他品牌、错误 major、UA-CH 缺失/冲突或直调 API 可进入产品 | 全部返回 426 `BROWSER_UNSUPPORTED`,只有最小引导,无登录、资源、DB 业务读取或外部调用,无绕过令牌 | API、E2E | +| `TDD-WP0-SUP-001` | 第二实例、端口占用、API/Worker 崩溃或退出留下残留进程 | mutex、固定端口、有限重启、degraded 状态、15 秒优雅退出和托盘允许动作符合 DevelopmentPlan | PKG-SEC、MANUAL | +| `TDD-WP0-DATA-001` | LocalDataRoot 位于仓库/项目/下载/素材根,或丢失后自动重建 | 路径校验拒绝违规目录;已初始化根缺失进入 data_missing;浏览器只见资源 ID | DB、API、PKG-SEC | +| `TDD-WP0-DATA-002` | 产品出现自动备份、项目包、业务导入或 P0-B 迁移 | 只允许下载原始生成图和 JPG/PNG;删除根或重装不可恢复;固定本机风险文案持续显示 | API、E2E、MANUAL | +| `TDD-WP0-STO-001` | 任一阈值使用模糊比例或边界错一字节 | 第 5.5 节七个精确字节值产生固定 notice/status | UNIT、DB | +| `TDD-WP0-STO-002` | 写入预计量用 `>=` 拒绝等号,或超限后仍有副作用 | `>` 才拒绝;等于上限提交成功后 full;超限不创建任务、冻结、文件关系或上游调用 | UNIT、DB、API | +| `TDD-WP0-STO-003` | full/unavailable 动作混淆或清理排队即减字节 | full 允许 JSON 保存与客户端仅下载;unavailable 拒绝新增后端写;物理删除和重测前计量不变 | DB、API、WRK、E2E | +| `TDD-WP0-FILE-001` | 文件流、数据库事务或进程崩溃产生可读半文件、孤儿引用或计量漂移 | 预检、staging 流式哈希/MIME、同卷原子改名、SQLite 提交、补偿队列和启动对账全覆盖;路径规范化拒绝 `..`、符号链接逃逸和 manifest 外对象 | DB、API、WRK | +| `TDD-WP0-CACHE-001` | 应用缓存超过 157,286,400 字节或含私有/内部内容 | LRU 在写前淘汰;只含 allowlist 公开资源;逐场景枚举 SW、Cache Storage、IndexedDB | UNIT、E2E | +| `TDD-WP0-EVT-001` | SSE 携带私有正文、被客户端当作业务真相或事件缺号后不恢复 | 事件只含固定非敏感字段;客户端收到后 REST refetch;断线/缺号 bootstrap;配置和 runtime 版本分离 | API、E2E | +| `TDD-WP0-SEC-001` | 凭据进入参数、环境、文件、日志或错误 | Supervisor 通过继承匿名管道最小传递;API/Worker 分离;缺失、清除、重启和扫描全通过 | PKG-SEC | +| `TDD-WP0-LOG-001` | 日志无限增长、含敏感数据或目录不可写后仍调用 AI | 10 MiB/10 文件/30 天轮转;白名单字段;写失败进入 unavailable 并阻断 AI | DB、WRK、PKG-SEC | +| `TDD-WP0-PKG-001` | ZIP 缺组件、包含真实配置或动态换端口 | 便携布局、SHA-256、START-HERE、RELEASE 记录和固定端口全部正确,制品零凭据 | PKG-SEC | + +WP-0 Green 不等于 AC-24 发布通过;真实浏览器记录必须在 WP-7 重新冻结。 + +### 6.2 WP-1 认证、权限、数据模型与审计基础 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP1-AUTH-001` | 有效邀请注册任一步部分成功 | 发码前和建号前复核;账号、初始 10 点、registration_grant、告知版本和邀请码次数同事务一次提交 | DB、API、E2E | +| `TDD-WP1-AUTH-002` | 无效/过期/停用/耗尽邀请码仍发码或占用次数 | 不发码、不建号、不赠点,返回稳定原因且无副作用 | DB、API、E2E | +| `TDD-WP1-AUTH-003` | 注册/登录对未注册、已注册、已注销、suspended 邮箱静默切换流程 | 六种状态严格按 PRD 分离入口;已注销邮箱只能新邀请重建且旧数据不恢复 | API、E2E | +| `TDD-WP1-AUTH-004` | 验证码重用、过期仍有效或会话未及时撤销 | 10 分钟一次性、60 秒重发、30 天会话;退出/暂停/注销/白名单移除立即撤销 | DB、API | +| `TDD-WP1-NOTICE-001` | 未勾选、版本缺失或告知遗漏第三方/本机数据处理仍可注册 | 告知含版本和生效日期,覆盖 AI 网关、Resend、高德、本机存储与无备份、管理员访问、DYN004、注销和 180 天规则;未同意不建号,成功时记录版本/时间 | UNIT、API、E2E、MANUAL | +| `TDD-WP1-SLOT-001` | suspended 不占名额、super_admin 占名额或第 11 人可注册 | active+suspended 普通账号最多 10;deleted 释放;super_admin 不占且不用邀请码 | DB、API、E2E | +| `TDD-WP1-ADM-001` | 白名单外发码、普通账号提升或全部管理员停用后自助恢复 | 后台独立 audience;多管理员可用;恢复仅安全配置;部署人员无产品身份 | DB、API、E2E | +| `TDD-WP1-CFG-001` | 安全配置修订部分生效或泄露白名单/HMAC | 完整验证后单一事务生效、撤销会话、system 审计和 revision 推进;失败整体回滚 | DB、PKG-SEC | +| `TDD-WP1-DEL-001` | 注销后仍可访问账号、项目、资源、坐标或会话 | 注销事务立即撤权和断链,物理内容异步清理,邮箱可作为全新账号重新注册 | DB、API、WRK、E2E | +| `TDD-WP1-DEL-002` | 匿名事件可回溯或注销延长访问审计期限 | 只保留允许字段和随机主体;匿名事件不晚于注销后 180 天;访问审计沿原 expires_at | DB、WRK | +| `TDD-WP1-AUD-001` | 普通连接可改删两类审计 | trigger 阻断 UPDATE/DELETE;仅保留期/隐私清理作用域可执行规定删除 | DB | + +真实 Resend 认证在 WP-7 才能把相关 AC 从 green 提升为 passed;开发预置账号永远不能成为正式认证证据。 + +### 6.3 WP-2 项目、点数与 GenerationJob + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP2-PROJ-001` | 新建/继续生成/历史上限混淆项目数量或比例 | 每次“新建创作”新项目;继续生成只追加;比例固定;10 张历史阻断追加但不阻断新项目 | DB、API、E2E | +| `TDD-WP2-PROJ-002` | 连续编辑并发保存、失败显示已保存或刷新恢复私有草稿 | 1 秒 debounce、单请求在途、最新快照合并;失败重试;强制退出只恢复 saved,撤销栈为空 | UNIT、API、E2E | +| `TDD-WP2-PROJ-003` | 回收站计时、20 项目恢复或 purge 可恢复边界错误 | purge_at 精确 +720 小时;满 20 保持 trashed;purged 立即不可访问并异步清理 | DB、API、WRK、E2E | +| `TDD-WP2-PROJ-004` | 旧标签覆盖、另存副本或多次冲突导出 | CAS 返回 412;旧页只读;仅一次本地导出且不写 latest;刷新后端最新状态 | API、E2E | +| `TDD-WP2-PROJ-005` | 重试新建项目或成功项目混入 failed_empty 批量删除 | 同一草稿每次重试只新增任务;仅无成功图草稿可批量入回收站 | DB、API、E2E | +| `TDD-WP2-CRED-001` | 点数不足、重复结算或生成流程制造负数 | 不足不建任务/流水/上游;reserve/commit/release 各一次;生成流程 available 不为负 | DB、API、WRK | +| `TDD-WP2-CRED-002` | 管理员调点修改 reserved 或缺原因/审计 | 只改 available,可为负;原因、流水和 AdminOperationLog 同事务 | DB、API、E2E | +| `TDD-WP2-GEN-001` | 并发提交创建两个任务、项目、参考关系或冻结 | `BEGIN IMMEDIATE`+部分唯一索引只保留一个 queued/running;后续返回当前任务 | DB、API | +| `TDD-WP2-GEN-002` | 成功/失败/拒绝与点数、项目历史或重试不一致 | 成功一图并 commit;失败/rejected 无输出并 release;重试同项目新任务 | DB、API、WRK、E2E | +| `TDD-WP2-GEN-003` | 旧 model_config_version 静默按新成本或限制提交 | 返回 412、最新摘要;不建任务、不上传引用、不冻结、不调用上游;重新确认后才提交 | API、E2E | +| `TDD-WP2-REF-001` | 未提交参考图被持久化或重试覆盖旧关系 | 提交前只在内存;每个任务保存独立关系快照;purged 后立即不可读并异步删除 | DB、API、E2E | +| `TDD-WP2-EXP-001` | 项目/成品保存失败撤回下载或覆盖旧 latest | 同一次 Blob 用于下载和上传;下载可成功、latest 保持旧值;刷新只恢复 saved | API、E2E | +| `TDD-WP2-EXP-002` | 从未导出项目自动生成默认成品 | 显示“暂无导出成品”,只提供进入编辑导出和下载原图 | API、E2E | +| `TDD-WP2-BAL-001` | 模型/账户/未知余额影响改写配置或重复释放 | 按影响范围只改 runtime;只 release 一次;未确认恢复前不可恢复 | DB、API、WRK | +| `TDD-WP2-ERR-001` | 九类错误被新增、归并、泄露原文或错误处理点数 | 三模型统一固定 category;提交前无任务,提交后只 release 一次;状态和用户动作符合第 9 节 | UNIT、API、WRK、E2E | + +### 6.4 WP-3 三模型契约验证与适配 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP3-MDL-001` | 初始集合零/多默认、优先级非法或 runtime 假装 verified | 三模型固定 seed;一个 enabled default;priority 1/2/3;contract unverified 时 runtime 不可用 | DB、API | +| `TDD-WP3-MDL-002` | 停用默认、重复优先级或 CAS 冲突产生部分配置 | 完整三模型集合原子校验和切换;失败原集合、runtime 和审计结果符合协议 | UNIT、DB、API、E2E | +| `TDD-WP3-MDL-003` | runtime 降级改变 enabled/default/priority 或推荐为空时写回配置 | 配置字段不变;推荐只从可用候选按 priority 派生,可为 null | UNIT、DB、API、E2E | +| `TDD-WP3-ADP-001` | 一个模型 fixture 被复用于另两个模型或静默接受多图 | 三个独立 Adapter 分别验证纯文字、参考图、四比例、同步/异步、响应和一图约束 | EXT-MOCK、EXT-REAL | +| `TDD-WP3-ADP-002` | 契约字段变化后仍为 verified | 端点、字段、参考图、响应、异步查询或错误协议任一变化立即 unverified,完整重验后才恢复 | DB、EXT-MOCK、EXT-REAL | +| `TDD-WP3-WRK-001` | Worker 崩溃后重新 submit、重复结算或丢任务 | 租约/心跳;有引用只 poll;无安全引用则 unknown_retryable+release+pending_manual_review | DB、WRK | +| `TDD-WP3-BAL-001` | 余额恢复自动执行或成本核对补扣用户点数 | model/account/unknown 影响正确;人工确认恢复并审计;成本核对不改任务终态或用户点数 | DB、WRK、E2E | + +三个模型任一缺少最后一次配置下的真实证据时,WP-3 保持 externally_blocked。 + +### 6.5 WP-4 桌面编辑器与导出 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP4-BG-001` | 换底图丢覆盖层、保留旧调色或色卡不刷新 | 覆盖层位置/尺寸保留;底图参数重置;色卡重提;一次撤销和保存 | UNIT、E2E | +| `TDD-WP4-BG-002` | 填充/适配/裁剪/缩放/定位或亮度、对比度、饱和度、色温、锐度、预设滤镜、恢复原图未保存、不可撤销或未进入导出 | 每项参数可预览、提交、撤销、自动保存并在重开与导出中一致;恢复原图只重置底图处理,不改变覆盖元素 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-CAN-001` | 选择循环、多选、层级、吸附或第 51 元素错误 | 50 元素可编辑保存导出;第 51 个阻断;无图层面板/隐藏/锁定/编组 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-TXT-001` | 多行丢换行、空内容提交或切模板改变变换 | 内容/换行持久;模板切换重置默认样式但保留位置/旋转/整体尺寸,可撤销 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-TXT-002` | 字体静默回退、字号与控制柄不同步、文字裁切、范围步长错误或背景透明度影响文字 | 真实归档字体;字号与缩放同步且内容重新测量不裁切;行距/字距/描边等精确范围;完成为一次撤销;重新打开和导出一致 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-TXT-003` | 未选模板即可添加普通文字,或模板分类、显示名称搜索、catalog 顺序、账号最近使用漂移 | 只能从模板添加;四类、显示名称搜索、稳定顺序和账号级最近使用符合 PRD;普通贴纸不复用该搜索 | UNIT、E2E | +| `TDD-WP4-STK-001` | 普通贴纸变换不进入保存/撤销/导出 | 移动、缩放、旋转、翻转、透明度、复制、层级和多选一致 | UNIT、E2E | +| `TDD-WP4-COL-001` | 色卡随机、读取处理后底图或额外上传图片 | 同图 `mmcq-v1` 五色确定;裁剪/调色/滤镜不刷新;换底图刷新;网络上传为 0 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-DYN-001` | 时间贴纸重开后变成新时间 | 使用插入/确认值并持久化,导出静态不可点击内容 | UNIT、E2E | +| `TDD-WP4-DYN-002` | DYN004 提示前调用定位、拒绝后插入或删除后留坐标 | 同意后才定位并保存坐标;拒绝调用为 0;手动地点回退;删除/注销清理 | UNIT、E2E | +| `TDD-WP4-DYN-003` | 身份贴纸重复 `@@`、覆盖修改账号或 DYN012 静默换字体 | 前缀一个;实例覆盖独立;DYN012 固定 FONT081 且证据标记替代 | UNIT、E2E、MANUAL | +| `TDD-WP4-EXP-001` | 四比例像素、JPG/PNG、未提交修改或同 Blob 规则错误 | 精确像素、sRGB、无水印、PNG 无损、JPG 80-100 默认 92;未提交文字或样式经确认后,提交形成且仅形成一次撤销记录并进入正常自动保存,再用提交后的同一画布状态合成;真正的导出动作不额外创建撤销记录 | UNIT、E2E、VIS-PERF | +| `TDD-WP4-A11Y-001` | 关键动作不可键盘到达、焦点逃逸或状态只靠颜色 | 焦点顺序、ARIA、live region、弹层返回、等价画布工具、axe critical/serious 为 0 | E2E、VIS-PERF | +| `TDD-WP4-VIS-001` | Chrome/Edge 白名单结构或导出漂移 | 固定环境按第 10 节阈值比较;已知替代项人工复核 | VIS-PERF、MANUAL | +| `TDD-WP4-PERF-001` | 50 元素、自动保存、资源面板或导出超过预算 | 第 10.2 节全部预算通过,失败不破坏项目或 latest export | VIS-PERF | + +### 6.6 WP-5 素材转换、发布与 P0-A 白名单 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP5-CAT-001` | 计数、part、重复项、顺序或懒加载错误 | part1-25 共 1,407 PNG;3 组重复保留;虚拟列表节点不线性增长,原图按需加载 | UNIT、E2E、MANUAL | +| `TDD-WP5-MAN-001` | 编译器读取证据目录、执行 Lua/Prefab、复制全库或改写源 | 只读规范入口;声明式转换;源前后哈希/时间不变;无等价全库副本 | UNIT、PKG-SEC、MANUAL | +| `TDD-WP5-WHITE-001` | 普通用户发现白名单外复杂素材或缺少规定白名单 | 精确 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 普通贴纸 | API、E2E | +| `TDD-WP5-COL-001` | P0-A 启用非四种色卡或四种布局不消费同一五色 | 只启用 COLOR001/002/008/016,统一五色,刷新规则与 WP-4 一致 | UNIT、E2E、VIS-PERF | +| `TDD-WP5-RES-001` | 公开 manifest 暴露 internal/private、路径可推导或缓存策略混用 | 三类路由、鉴权和 cache header 分离;浏览器永不获得绝对路径 | API、E2E、PKG-SEC | +| `TDD-WP5-UPL-001` | 后台上传未流式校验、原图/缩略图漏计量或 full 仍写入 | PNG/WebP、稳定 ID、part、顺序和 MIME 校验;两类文件分别预留并计量 | DB、API、E2E | +| `TDD-WP5-CLN-001` | 自动清理、有引用仍删除、整批部分排队或排队即减容量 | 候选、同管理员确认、BEGIN IMMEDIATE 复核、整批拒绝/排队、物理删除、重测和全阶段审计 | DB、API、WRK、E2E | +| `TDD-WP5-PREV-001` | 授权改变角色、公开 manifest 泄漏或撤销后仍可取资源 | 每次校验 grant;no-store;撤销立即阻断新访问;已有项目按版本渲染 | DB、API、E2E | +| `TDD-WP5-CACHE-001` | SW 拦截 internal/private 或 IndexedDB 保存私有字段 | 仅公开 allowlist;每场景实际枚举 registration、Cache keys 和 IndexedDB 记录 | E2E | + +真实 3.57 GB 归档只在受控 release job 只读校验;日常 CI 使用小型合成 fixture。 + +### 6.7 WP-6 运营后台与内部运营控制 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP6-ADM-001` | 普通/预览账号进入后台、后台显示私有正文或敏感配置 | active super_admin 服务端授权;默认仅安全摘要;角色和会话域严格分离 | API、E2E | +| `TDD-WP6-PRIV-001` | 告知确认只存在 Cookie/session/localStorage 或同版本重复 | UserProfile 按 admin+version 持久;跨标签、浏览器/API 重启、重登和 Chrome/Edge不重复;升版重阻断 | DB、API、E2E | +| `TDD-WP6-PRIV-002` | hover/预取打开内容、确认替代逐次审计、要求填写访问目的/原因或审计失败仍返回内容 | 每个入口原样显示“打开并记录审计”;每次点击先提交独立 PrivateContentAccessLog;UI 和 API 均不要求逐次填写访问目的、原因或二次确认;审计只记录 PRD/DevelopmentPlan 允许的对象、管理员、时间、内容类型等非正文信息;失败不返回;列表/HTML/缓存无正文 | DB、API、E2E | +| `TDD-WP6-SVC-001` | Resend 发第 81 封/超月限、高德发第 1001 次或暂停后伪造成功 | 配额领取事务化;paused_quota/provider;已有会话继续;DYN004 停而手动地点继续 | DB、API、EXT-MOCK、E2E | +| `TDD-WP6-SVC-002` | 后台提高上限、启用收费或自动恢复 | 只允许不扩大能力的下调并审计;提高/付费/自动扩容被拒绝;恢复需前置确认 | DB、API、E2E | +| `TDD-WP6-AUD-001` | 敏感操作无日志、两类日志混用、含正文或 180 天内可删 | 全部规定操作同事务审计;贴纸清理全阶段;两类表分离;到期 Worker 清理 | DB、API、WRK、E2E | +| `TDD-WP6-STATE-001` | 后台把 configured default、runtime availability 和 recommended 合并 | 三列独立;临时异常不改配置;恢复只重新派生推荐 | API、E2E | +| `TDD-WP6-DIAG-001` | 总览/服务/存储/诊断暴露路径、密钥或正文 | 只显示固定非敏感字段、计量和状态;复制诊断通过同一脱敏扫描 | API、E2E、PKG-SEC | + +### 6.8 WP-7 P0-A 验收准备与发布 + +| 测试族 | 首个 Red | Green 断言 | 层级 | +| --- | --- | --- | --- | +| `TDD-WP7-REL-001` | `RELEASE.json` 缺失/漂移、ZIP 哈希或真实浏览器未验收 | 发布记录、制品、START-HERE、Chrome/Edge full version、固定 major 和 Windows build 一致 | PKG-SEC、E2E、MANUAL | +| `TDD-WP7-EXT-001` | 任一模型缺独立真实契约证据 | 三模型各自完成纯文字、参考图、四比例、同步/异步、响应、九类错误和结算证据 | EXT-REAL、MANUAL | +| `TDD-WP7-EXT-002` | Resend 域名/免费规则/送达门槛未证实 | 依据 PRD 第 23.2 节、AC-41、AC-47 与 DevelopmentPlan 第 1.4、11.1 节:SPF/DKIM;QQ、163、企业邮箱各 20 封至少 19 封在 2 分钟内送达;正式账号真实认证 | EXT-REAL、MANUAL | +| `TDD-WP7-EXT-003` | 高德接口、绑定、安全限制或 1,000 次硬上限未证实 | 定位/逆地理、QPS、白名单、安全和应用硬停止实测通过 | EXT-REAL、MANUAL | +| `TDD-WP7-UI-001` | UIDesign 页面/入口或适用状态无实现落点,或 1080p/200% 出现遮挡 | 第 11.1 节页面 × 状态矩阵全部适用单元格有证据;Pencil 仅作布局参考,Canvas Index 不计产品实现;固定文案、焦点、状态、控件和无重叠检查通过 | E2E、VIS-PERF、MANUAL | +| `TDD-WP7-SEC-001` | 制品、日志、DB 配置、manifest 或文档出现真实敏感值/用户路径 | 全制品扫描零命中;凭据只在 Windows Credential Manager;诊断脱敏 | PKG-SEC | +| `TDD-WP7-AC-001` | 52 项 AC、89 个测试族或 117 个规范 case 缺失,重复主责、孤儿 case 或用 P0-B 结果冒充 | 第 12 节矩阵每项一个主责且证据齐全;89/89 父族有 case 且派生属性完整;未来四项不计入;四份全局来源哈希未变,UI 参考变化按第 1.2.2 节局部重验 | UNIT、MANUAL | + +任一真实外部门槛失败时不得开始 P0-A 实际验收,也不得把 WP-7 标记 passed。 + +### 6.9 高风险可执行 case + +第 6.1 至 6.8 节的 89 个测试族是覆盖导航,不代替逐场景用例。第 6.9.2 至 6.9.9 节给出重点能力的细分 case,第 6.9.10 至 6.9.17 节为其余父族给出至少一个规范 case;合计 117 个 case,保证 89/89 父测试族均有执行入口。同一测试族的参数分支仍按第 2.2 节展开。实现不得删除 case、合并掉禁止副作用断言,或用一条端到端 happy path 代替 DB/API 分层断言。 + +#### 6.9.1 通用执行约定 + +- API case 默认使用已通过浏览器门禁的请求上下文、`Host: 127.0.0.1:43121`、同源 Origin、正确 audience 的隔离 session、页面加载取得的 CSRF token;所有创建型、金融或文件副作用请求使用稳定的独立 `Idempotency-Key`。case 明确测试门禁或鉴权失败时才替换这些默认值。 +- `REGISTER_SEND`、`REGISTER_COMPLETE`、`LOGIN_SEND`、`LOGIN_COMPLETE`、`PRIVATE_OPEN` 和 `EXPORT_SAVE` 是测试别名,分别在生成的 OpenAPI 中解析到 `/api/v1/auth/register/*` 发码/完成、`/api/v1/auth/login/*` 发码/完成、`/api/v1/admin/private-content/*` 单对象打开和 `/api/v1/projects/{id}/exports` latest 成品保存 operation。别名解析不到唯一 operation、请求或响应不符合 TypeBox/OpenAPI 时先使 `TDD-WP0-API-001` 失败;不得为满足用例新增产品路由。 +- 表中 `case_id` 就是第 2.2 节的可执行 `test_id`。父测试族必须先与第 6.1 至 6.8 节的已知父族集合匹配,再用正则 `^(TDD-WP[0-7]-[A-Z0-9]+-[0-9]{3})-` 解析,且捕获组 1 必须是该已知父族;不得通过删除最后一个命名段或按最后一个连字符截断。`result.json` 必须逐项记录第 2.2 节全部字段,包括父测试族继承的 WP、层级、自动/人工属性和发布门禁;不能只记录测试族汇总结果。 +- 每个 case 在临时 SQLite、临时 LocalDataRoot、可控 UTC 毫秒时钟和独立浏览器 profile 中运行。步骤 0 固定保存脱敏 DB/文件基线;最后一步固定复查余额不变量、活动任务唯一索引、审计不可变约束和证据脱敏。 +- 证据根固定为 `artifacts/tdd//cases//`。每个目录必须有 `result.json`;API case 增加 `request.json`、`response.json`,DB case 增加 `db-diff.json`,文件 case 增加 `fs-before.json`、`fs-after.json`,Worker case 增加 `worker-events.json`,E2E case 增加 `trace.zip` 和 `screenshots/`。文件只保留 Schema 白名单字段、哈希和逻辑资源 ID,不保留正文、图片、邮箱、验证码、令牌或绝对路径。 +- 表中的“响应”同时是 HTTP 与 UI 断言:HTTP 状态、错误信封和字段以冻结 OpenAPI 为准;上游只冻结语义而未冻结具体成功状态码/字段名时,case 断言生成 Schema 所定义的成功响应及列出的业务结果,不在本文件发明字段。 + +#### 6.9.2 认证 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP1-AUTH-001-valid-register` | AUTH-01 至 04、AC-01 | `FX-INVITE-VALID`、`FX-CHALLENGE`;占名额普通账号为 9;未注册 `.invalid` 邮箱 | 1. 调 `REGISTER_SEND`;2. 读取模拟 Resend 验证码;3. 以署名、社交 ID、当前告知 version/同意和同一 registration_id 调 `REGISTER_COMPLETE`;4. 调普通 session | 两次均符合成功 Schema;注册后得到普通 audience 的 30 天会话并进入创作,点数显示 available 10/reserved 0 | `users/user_profiles/credit_accounts/privacy_consents` 各新增正确关系;`invite_codes.used_count +1`;challenge 只消费一次;新增 session 只存 token 摘要;初始点数流水一次 | 不创建 super_admin;不保存验证码明文;不重复赠点或核销邀请码;不产生第二会话/第二注册实体 | `request.json`、`response.json`、`db-diff.json`、`trace.zip`、`screenshots/registered.png` | +| `TDD-WP1-AUTH-002-invalid-invite` | AUTH-01、02、AC-02 | 参数化 `FX-INVITE-EXPIRED/DISABLED/EXHAUSTED` 和不存在的邀请码 | 1. 对每个参数调 `REGISTER_SEND`;2. 检查 Resend mock 调用;3. 查询认证相关表 | 返回冻结的非成功错误语义并在邀请码字段显示原因,第二阶段不展开 | `users/user_profiles/credit_accounts/privacy_consents/email_challenges/sessions` 无新增;邀请码计数不变 | Resend 调用为 0;不建号、不赠点、不占名额、不写成功审计或成功提示 | 每参数 `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/error.png` | +| `TDD-WP1-AUTH-001-final-recheck-race` | AUTH-01、NFR-04、AC-01、02、45 | `FX-INVITE-VALID` 已成功发码;随后参数化为邀请码耗尽/停用/过期或第 10 个名额被另一事务占用 | 1. 完成前改变可变条件;2. 调 `REGISTER_COMPLETE`;3. 用相同 Idempotency-Key 重试 | 返回对应冻结错误;不建立 session,不进入创作;相同重试返回同一失败结果 | `BEGIN IMMEDIATE` 后无新 user/profile/credit/consent/session;邀请码次数不增加;名额统计不变 | 不出现账号已建但邀请码未核销、邀请码已核销但账号未建或初始点数孤儿流水 | `response.json`、`db-diff.json`、`transaction-trace.json` | +| `TDD-WP1-AUTH-003-entry-state-matrix` | AUTH-01、02、05、06、AC-33 | 参数化未注册、active 已注册、deleted、suspended 邮箱;注册和登录两个入口 | 1. 每种邮箱分别调注册发码与登录发码;2. 对允许分支完成验证码;3. 检查页面不自动切入口 | 行为严格匹配 PRD AUTH-02 状态矩阵;deleted 仅可新邀请注册;suspended 均提示联系管理员 | 只允许的分支创建 challenge/session/new user;重复注册不核销邀请码;deleted 新建主体与旧主体无关联 | 不静默注册/登录转换;不恢复 deleted 数据;不因 suspended 释放名额;错误不泄漏后台白名单 | 每参数 `response.json`、`db-diff.json`、`trace.zip`、`screenshots/entry-state.png` | +| `TDD-WP1-AUTH-004-challenge-guards` | AUTH-02、04、AC-33 | `FX-CHALLENGE`、可控时钟、active 普通账号 | 1. 成功消费验证码;2. 重放同码;3. 新建 challenge 后前进超过 10 分钟再提交;4. 发码后 60 秒内再发;5. 连续错误触发限流 | 仅首次有效提交成功;重放/过期/错误使用稳定错误;60 秒重发和高频为 429;页面不显示假成功 | 首次 challenge `consumed_at` 只写一次;失败次数/限流记录按规则变化;失败分支不新增 session | 不记录验证码明文;不延长原 challenge;限流失败不调用 Resend、不创建会话 | 每参数 `response.json`、`db-diff.json`、`external-calls.json` | +| `TDD-WP1-AUTH-004-session-revocation` | AUTH-04 至 07、AC-22、33、49 | 每个参数使用独立库:active 普通用户或 active super_admin 各有 2 个 session | 1. 验证正常 session;2. 分别触发普通用户 logout/暂停/注销或管理员 logout/停用/移出白名单;3. 再调受保护 API | 撤销前成功;每种撤销后均为 401 或相应身份入口,已打开页面退出旧数据;两种 audience 不能互用 | 相应主体全部 session `revoked_at` 在同一事务写入;账号/白名单状态按触发操作变化 | 不只撤销当前浏览器;不把普通 session 提升为后台 session;不保留可交互私有响应 | 每参数 `response.json`、`db-diff.json`、`trace.zip`、`screenshots/revoked.png` | + +#### 6.9.3 点数 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP2-CRED-001-reserve` | CREDIT-01 至 05、AC-03 | `FX-CREDIT-ENOUGH`、`FX-MODEL-VERIFIED`,无活动任务 | 1. 记录余额;2. 以成本 1 调 `POST /api/v1/generations`;3. 查询 `/me/credits` 和流水 | 创建任务成功;UI 显示 available 9/reserved 1 和 queued;流水显示 generation_reserve | `credit_accounts` 原子 10/0→9/1;一条 reserve ledger、一个 queued job 和 outbox 同事务 | 不提前生成 commit;不使总点数改变;不出现任务已建但未冻结或冻结无任务 | `request.json`、`response.json`、`db-diff.json` | +| `TDD-WP2-CRED-001-insufficient` | CREDIT-04、AC-28 | `FX-CREDIT-INSUFFICIENT`,成本 1 | 1. UI 检查生成按钮;2. 直接调生成 API 防绕过;3. 查询 DB/网关 mock | UI 禁用并显示 available/reserved/required/联系管理员;API 返回冻结错误语义 | 余额、ledger、job、project、reference、outbox 均无变化 | 上游调用 0;无 storage reservation/staging 文件;不产生负余额 | `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/insufficient.png` | +| `TDD-WP2-GEN-001-concurrent-reserve` | CREDIT-03、GEN-07、AC-05、29 | `FX-CREDIT-ENOUGH`,两个不同 submission/idempotency key 同时开始 | 1. 用 barrier 并发提交两次;2. 等待响应;3. 查询 current job、余额和流水 | 一个响应创建任务,另一个 200 返回同一现有任务引用;UI 提供返回当前任务 | 仅一个 queued/running job、一个 project(new_project 模式)、一条 reserve ledger;余额只转移一次 | 不创建第二项目/参考关系/outbox/upstream call;available 不为负 | 两份 `response.json`、`db-diff.json`、`concurrency-trace.json` | +| `TDD-WP2-CRED-002-admin-adjustment` | CREDIT-05、06、ADMIN-01、09、AC-29、50 | `FX-JOB-ACTIVE`;available 0/reserved 1;`FX-ACTOR-ADMIN-A` | 1. 管理员填写原因扣减 5;2. 用同 adjustment_id 重试;3. 用户读点数 | 首次成功,重试返回同一结果;用户显示 available -5/reserved 1;无充值入口 | available 0→-5,reserved 保持 1;一条 admin_adjustment ledger 和一条 AdminOperationLog 同事务 | 不改活动任务/冻结流水;不重复调点;不允许空原因;不使生成流程自身制造负数 | `response.json`、`db-diff.json`、`screenshots/negative-balance.png` | +| `TDD-WP2-CRED-001-finalize-once` | CREDIT-03、05、GEN-09、10、AC-03、04、29 | 参数化成功、技术失败和安全拒绝的 `FX-JOB-ACTIVE` | 1. Worker 提交终态;2. 重复 callback/poll;3. 模拟 Worker 重启再处理 | 成功为 succeeded;技术失败为 failed;安全拒绝为 rejected;用户余额/动作匹配固定类别 | 每任务恰一条 commit 或 release;reserved 恰减一次;成功才建 output/history,失败/拒绝 available 恢复 | 不双重 commit/release;不在失败建 output;终态不可逆;不保存供应商原文 | `worker-events.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | + +#### 6.9.4 GenerationJob + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP2-GEN-001-submit-snapshot` | GEN-01 至 08、PROJECT-01、AC-03、34、44 | `FX-MODEL-VERIFIED`、`FX-CREDIT-ENOUGH`、`FX-REF-VALID`,new_project | 1. multipart 提交 prompt/model/ratio/config version/cost/reference;2. 查询任务;3. 检查 staging/正式文件 | 成功响应为 queued,返回同一任务真相;UI 不显示假百分比 | 新 project、job、reference snapshot、reserve ledger、outbox 同事务;提交后 reference 从 staging 进入私有 managed file | 不接受未冻结高级字段;不让 Worker 领取未完整提交任务;不暴露路径;失败补偿不留下可读半文件 | `request.json`、`response.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | +| `TDD-WP2-GEN-002-success` | GEN-06、08、11、CREDIT-03、AC-03 | 上一 case 的 queued job + `FX-GATEWAY-SYNC` | 1. Worker 领取;2. 调上游并验证单图;3. 落盘;4. 查询任务/项目 | queued→running→succeeded;项目可进入编辑且历史新增一图 | lease/heartbeat、output managed file、output_asset、generation_commit、project history/draft_state 一次提交;容量增加实测字节 | 不在文件安全落盘前 succeeded;不保存多图;不重复扣点;不改变项目比例 | `worker-events.json`、`db-diff.json`、`fs-after.json`、`screenshots/succeeded.png` | +| `TDD-WP2-ERR-001-upstream-timeout` | GEN-09、16、17.18、AC-04、53 | 独立 `upstream_timeout` fixture;已提交任务超时 | 1. 触发超时;2. 检查唯一下一步为“使用原输入重试”;3. 点击该动作;4. 查询原任务、新任务和项目 | 原任务 failed/category=`upstream_timeout`;只提供“使用原输入重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次且无 output;新任务独立 reserve 并保存相同输入关系快照 | 不改成“稍后重试”;不要求修改输入/选择模型/联系管理员;不复用原任务 ID 或创建第二项目 | `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/upstream-timeout.png` | +| `TDD-WP2-ERR-001-upstream-failed` | GEN-09、16、17.18、AC-04、53 | 独立 `upstream_failed` fixture;已提交任务临时失败 | 1. 触发临时失败;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询原任务、新任务和项目 | 原任务 failed/category=`upstream_failed`;只提供“稍后重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次且无 output;新任务独立 reserve 和关系快照 | 不显示“使用原输入重试”或输入/模型修改动作;不自动重试、不创建第二项目 | `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/upstream-failed.png` | +| `TDD-WP2-ERR-001-safety-rejected` | GEN-10、16、17.18、AC-04、53 | 供应商明确安全拒绝证据;与 reference fixture 完全分离 | 1. 触发安全拒绝;2. 检查唯一下一步为“修改提示词或参考图”;3. 不修改输入尝试继续;4. 修改提示词或参考图后提交 | 原任务 rejected/category=`safety_rejected`;未修改时不能新提交;修改后同项目新增 GenerationJob | 原任务 release 一次、无 output;修改前无新 job/reserve;修改后新任务保存修正快照 | 不提供原样/稍后重试、选择模型或联系管理员;不保存违规输出;不映射为 reference_invalid | `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/safety-rejected.png` | +| `TDD-WP2-ERR-001-model-disabled` | GEN-07、16、17.18、AC-30、53 | `model_disabled` 创建前 fixture;参数化存在/不存在其他 enabled 模型 | 1. 触发创建前阻断;2. 检查下一步仅“选择其他模型或等待”;3. 有可用模型时显式改选并确认;4. 无可用模型时保持页面等待 | 返回 category=`model_disabled` 且不创建任务;改选后才能提交;无模型时保持禁用 | 阻断分支无 job/project/reference/reserve/outbox;改选分支按新模型快照创建 | 不提供联系管理员、原输入重试或稍后重试;不自动切推荐或把 runtime 状态写回配置 | `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/model-disabled.png` | +| `TDD-WP2-ERR-001-gateway-balance` | GEN-15、16、17.17、17.18、AC-40、51、53 | `gateway_balance_insufficient` 创建前/后 fixture;明确未受影响模型集合 | 1. 触发余额不足;2. 检查下一步仅“选择未受影响模型或联系管理员”;3. 显式选择未受影响模型并重新确认;4. 无候选时检查联系管理员 | 返回/保存 category=`gateway_balance_insufficient`;已提交任务 failed;只有改选未受影响模型后才能新提交 | 创建前无 reserve/job;创建后 release 一次;新任务使用改选模型快照;runtime 影响范围按 GEN-15 更新 | 不提供等待、原输入/稍后重试或任意“其他模型”;不自动充值、不用 Dada 点数恢复、不回写 ModelConfig | `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/gateway-balance.png` | +| `TDD-WP2-ERR-001-gateway-contract` | GEN-13、16、17.18、AC-40、53 | `gateway_contract_invalid` 创建前/后 fixture;另一个 contract verified 模型 | 1. 触发契约未验证/失效;2. 检查下一步仅“选择其他模型或联系管理员”;3. 显式改选已验证模型并重新确认;4. 无候选时检查联系管理员 | 返回/保存 category=`gateway_contract_invalid`;已提交任务 failed;改选 verified 模型后才能新提交 | 创建前无 reserve/job;创建后 release 一次;失效模型 contract 状态按契约更新;新任务保存新模型/版本 | 不提供等待、原输入/稍后重试;不让未验证模型提交;不自动切推荐或部分改写配置 | `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/gateway-contract.png` | +| `TDD-WP2-ERR-001-reference-invalid` | GEN-05、16、17.18、AC-04、44、53 | 格式/大小/数量和非安全型内容不可用四组 fixture;不得使用安全拒绝响应 | 1. 分别触发创建前/后无效;2. 检查唯一下一步为“更换或移除参考图”;3. 不改参考图尝试继续;4. 更换/移除后提交 | category=`reference_invalid`;未修改时不创建新任务;更换/移除后同项目新增 GenerationJob | 创建前无 job/reserve;创建后 failed+release 一次;修正后保存新的 reference snapshot | 不提供修改提示词、重试、等待、选模型或联系管理员;不映射为 safety_rejected | 每 fixture `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/reference-invalid.png` | +| `TDD-WP2-ERR-001-unknown-retryable` | GEN-09、16、17.18、AC-04、53 | 独立 `unknown_retryable` fixture;已提交任务 | 1. 触发未知临时错误;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询任务和脱敏诊断 | 原任务 failed/category=`unknown_retryable`;只提供“稍后重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次、无 output;新任务独立 reserve;只保留非敏感诊断 | 不显示“使用原输入重试”、修改输入、选模型或联系管理员;不泄漏原始错误或自动重试 | `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/unknown-retryable.png` | +| `TDD-WP2-ERR-001-unknown-non-retryable` | GEN-09、16、17.18、AC-04、53 | 独立 `unknown_non_retryable` fixture;已提交任务 | 1. 触发未知不可重试错误;2. 检查唯一下一步为“联系管理员”;3. 尝试寻找或调用重复提交动作;4. 查询任务 | 原任务 failed/category=`unknown_non_retryable`;只提供“联系管理员”,无重试或修改入口 | release 一次;无 output;原草稿保留但本 case 不创建新 job | 不自动重试、不循环轮询制造新任务、不降级为 unknown_retryable、不提供其他类别动作 | `response.json`、`worker-events.json`、`db-diff.json`、`screenshots/unknown-non-retryable.png` | +| `TDD-WP2-GEN-003-stale-config` | GEN-15、ADMIN-03、AC-30 | 用户已确认旧 `model_config_version/cost`;管理员原子发布新集合 | 1. 旧页面直接提交;2. 读取 412 摘要;3. 重新 GET models 并明确确认;4. 再提交 | 第一次 412 且要求重新确认;第二次才可创建任务 | 第一次 job/project/reference/reserve/outbox 全无变化;第二次按新快照创建 | 第一次不上游、不持久化参考图、不冻结;不自动替用户切推荐模型 | `response.json`、`db-diff.json`、`external-calls.json`、`trace.zip` | +| `TDD-WP3-WRK-001-recovery` | GEN-08、09、CREDIT-03、AC-04、51 | 参数化 `FX-GATEWAY-ASYNC` 有引用和 `FX-GATEWAY-UNKNOWN` 无安全引用;在 submit 后终止 Worker | 1. 使租约过期;2. 重启 Worker;3. 有引用分支 poll;4. 无引用分支终结;5. 再重启复查 | 有引用不重新 submit,只 poll 到真实终态;无引用为 failed/unknown_retryable,后台显示成本核对待人工 | 租约重新领取;无引用 `upstream_outcome_known=false`、`upstream_cost_reconciliation=pending_manual_review`,release 一次 | 不把 pending_manual_review 写入 job status;不补扣用户点数;不重复上游 submit/结算 | `worker-events.json`、`db-diff.json`、`external-calls.json` | + +#### 6.9.5 容量 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP0-STO-002-equal-limit` | 17.19、NFR-05、AC-55 | 已计量+活动预留=`5,368,709,119`,预计写 1 字节,可写目录 | 1. 调受管二进制写入预检;2. 提交文件/DB;3. 重读 storage state | 不得返回 507;写入成功后 notice/status 为 full | 建立并消费一份 reservation;managed bytes 精确为 `5,368,709,120`;managed file 已提交;last_measured_at 更新 | 不用 `>=` 拒绝;不回滚已成功等号写入;不调用自动清理 | `response.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | +| `TDD-WP0-STO-002-over-limit` | 17.19、NFR-05、AC-55 | 已计量+活动预留=`5,368,709,119`,预计写 2 字节 | 1. 分别尝试生成/参考图/export/贴纸原图/缩略图;2. 查询所有副作用 | 每次 507 `STORAGE_CAPACITY_EXCEEDED`,返回非敏感状态和 remaining bytes | storage state 保持由实际量决定;无新 reservation/managed file/latest export/job/ledger/outbox | 上游调用 0;无 staging 残留;不把单个超大请求伪造成持久 full(若现有承诺仍低于上限) | 每路由 `response.json`、`db-diff.json`、`fs-after.json`、`external-calls.json` | +| `TDD-WP0-STO-003-full-matrix` | 17.19、PROJECT-04、EXPORT-06、AC-55 | managed bytes 恰为上限,status full,SQLite 可写 | 1. 保存纯 JSON project state;2. 读取/下载已有文件;3. 客户端合成并仅下载;4. 尝试 latest 保存和新生成;5. 永久删除/显式清理 | JSON 保存、读取、下载、仅下载、删除/清理入口可用;latest/新生成/其他二进制写为 507 | JSON state_version 可增加;禁止项不新增 managed file/job/ledger;删除/清理只先逻辑提交/排队 | 不自动删除;客户端下载不更新 latest_exports;排队不提前减少 managed bytes | `response.json`、`db-diff.json`、`fs-before.json`、`fs-after.json`、`trace.zip` | +| `TDD-WP0-STO-003-unavailable-matrix` | 17.19、NFR-05、AC-35、55 | LocalDataRoot 不可写,再参数化 SQLite 仍可写/不可写 | 1. 尝试 project JSON 保存、生成和二进制写;2. 读取/下载/仅下载;3. 尝试删除/清理 | 新增后端写全部失败且 UI 为 failed/unavailable;读取、下载和可行的仅下载保留;SQLite 不可写时删除/排队明确未提交 | 不更新 state_version/managed bytes/latest/job/ledger;SQLite 可写时允许规定逻辑删除/队列,反之无 DB 变化 | 不显示已保存;不上游;不把内存草稿写浏览器持久存储;不伪造清理完成 | `response.json`、`db-diff.json`、`trace.zip`、`screenshots/unavailable.png` | +| `TDD-WP0-STO-003-cleanup-remeasure` | ADMIN-06、17.19、AC-31、55 | full;查询时全部为无引用、可清理的历史贴纸文件 | 1. 查询候选并创建 intent;2. 参数 A 在确认前给其中一项新增 active/trashed 项目、已发布版本或待发布引用,再确认;3. 参数 B 保持全部无引用并确认;4. 读取排队容量;5. Worker 物理删除并重计量 | A 返回 `ASSET_HISTORY_REFERENCE_CONFLICT` 且整批不排队;B 整批排队,排队阶段仍 full,物理删除并重计量后才按真实值恢复 | A 只追加 `result=denied` 审计且 managed bytes 不变;B scheduled 前后 bytes 不变,物理成功后 files/queue/bytes/status/last_measured_at 同事务更新 | 不让查询时已有引用文件进入候选;不部分排队冲突批次;不在物理删除前减量;不自动创建 intent | 每参数 `response.json`、`db-diff.json`、`worker-events.json`、`fs-before.json`、`fs-after.json` | + +#### 6.9.6 私有内容访问 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP6-PRIV-001-ack` | ADMIN-05、17.1、AC-25 | `FX-ACTOR-ADMIN-A` 未确认 current notice v1 | 1. 进入私有区域;2. 得到 428;3. 以 expected v1 调 notice ack;4. 重试区域摘要 | 首次 428 `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED` 只含 version/message_key;ack 成功后区域仅显示安全摘要 | 只更新该 admin 的 notice version/ack time;幂等重试不新增语义变化 | ack 不创建 PrivateContentAccessLog;不返回正文/图片;不写 Cookie/localStorage 作为真相 | `response.json`、`db-diff.json`、`trace.zip`、`screenshots/notice.png` | +| `TDD-WP6-PRIV-001-persistence` | ADMIN-05、AC-25 | admin A 已确认 v1 | 1. 新标签;2. API 重启;3. 浏览器重启/重登;4. 在真实 Chrome 与 Edge 登录同一 admin | 同版本均不重复提示,session/bootstrap 计算 `notice_acknowledged=true` | UserProfile ack 字段不因浏览器/会话改变;每次 session 独立但 ack 不重复写 | Cookie/session/localStorage/IndexedDB 清空不使服务端遗忘;跨管理员不共享 ack | 每环境 `response.json`、`db-diff.json`、`trace.zip`、`screenshots/ack-persisted.png` | +| `TDD-WP6-PRIV-001-version-bump` | ADMIN-05、AC-25 | admin A/B 已确认 v1,应用 current notice 切到 v2 | 1. 两个 admin 分别进入区域;2. 尝试用 expected v1 ack;3. 读取 v2 并确认 | 两者重新 428;旧 expected version 失败且要求重读;各自确认 v2 后可进入摘要 | 旧 ack 保留历史语义但当前字段仅在正确 v2 ack 后更新;A/B 独立 | 不以旧确认放行;不因 A 确认替 B 确认;不返回私有正文 | `response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP6-PRIV-002-open-after-audit` | ADMIN-05、17.10、AC-25、50 | active admin A 已确认 current;目标含图片和完整提示词 | 1. 列表确认无正文;2. 不带 purpose/reason 调 `PRIVATE_OPEN`;3. 在提交钩子确认审计落库后才释放受控读取;4. 对同目标再打开 | 每个入口原样“打开并记录审计”;每次打开成功且各生成一条访问审计 | 每次先 append 一条含 admin/目标/时间/content_type/expires_at 的 PrivateContentAccessLog;内容本身不入审计 | UI/API 不要求目的、原因或二次确认;不预取/hover 打开;不把正文放列表、HTML、缓存或证据 | `response.json`、`db-diff.json`、`ordering-trace.json`、`trace.zip`、`screenshots/open-entry.png` | +| `TDD-WP6-PRIV-002-deny-without-audit` | ADMIN-05、17.10、AC-25 | 参数化未确认、普通用户/预览账号、停用 admin、审计 insert 失败 | 1. 调区域/`PRIVATE_OPEN`;2. 模拟审计约束失败;3. 枚举页面、SW、Cache、IndexedDB | 未确认 428;其他未授权 401/403/404 按冻结协议;审计失败返回非成功且无内容字节 | 未授权无访问审计;审计失败事务不留下伪成功记录,也不发放读取 | 不返回正文/图片/可推导 URL;不写 AdminOperationLog 替代;不因 notice ack 省略逐次审计 | 每参数 `response.json`、`db-diff.json`、`cache-enumeration.json`、`trace.zip` | + +#### 6.9.7 模型配置 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP3-MDL-001-seed-read` | GEN-01、ADMIN-03、17.2、AC-30 | 全新迁移后的 `FX-MODEL-SEED` | 1. 启动迁移;2. 调 `GET /api/v1/models`;3. 查集合约束 | 返回固定三模型;Flash 唯一 configured default;priority 1/2/3;recommended null;三者 contract unverified/runtime unavailable | 一个 current set,三个 immutable member/version,独立 runtime 行 | 不把 unverified 标为 available;不生成第二默认;不把 recommended 写配置 | `response.json`、`db-diff.json` | +| `TDD-WP3-MDL-002-replace-default` | ADMIN-03、17.2、AC-30 | `FX-MODEL-VERIFIED`,current set version N | 1. 完整提交三模型集合:停用当前默认并指定另一个 enabled default;2. 带 expected N/Idempotency-Key;3. GET models | 保存成功,读取始终恰一个 enabled default,config_set_version=N+1 | 新 immutable set/必要 config versions、AdminOperationLog/outbox 写入,最后原子切 current pointer | 不出现可见零默认/双默认中间态;不逐行部分保存;不改无关 runtime | `request.json`、`response.json`、`db-diff.json`、`transaction-trace.json` | +| `TDD-WP3-MDL-002-invalid-set` | ADMIN-03、17.2、AC-30 | 参数化缺替代、非法替代、priority 缺失/浮点/0/负数/重复、缺模型/未知模型 | 1. 分别 PUT 完整候选;2. 读取稳定错误;3. GET 当前集合 | 返回对应 400/409 稳定 code;UI 聚焦修正字段;当前集合仍为 N | current pointer、versions/runtime/审计成功记录/outbox 均无变化 | 不生成孤儿候选 set;不部分改变 enabled/default/priority;不新增错误分类 | 每参数 `response.json`、`db-diff.json` | +| `TDD-WP3-MDL-002-cas-conflict` | ADMIN-03、AC-30 | 两个 admin 基于 expected N 编辑不同完整集合 | 1. A 保存为 N+1;2. B 仍以 N 保存;3. B 刷新 | A 成功;B 返回 412 `MODEL_CONFIG_VERSION_CONFLICT` 和最新安全摘要 | 仅 A 的 immutable set、pointer、审计/outbox 存在;B 零写入 | B 不覆盖 A、不自动 merge、不复用过期配置继续提交 | 两份 `response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP3-MDL-002-contract-change` | GEN-13、15、ADMIN-03、AC-30、40 | verified 模型集合 | 1. 修改端点/字段/参考图/响应/异步查询/error mapping 任一契约字段;2. 保存;3. GET models;4. 尝试生成 | 保存成功但对应模型 contract unverified、runtime reason contract_unverified;生成被阻断直到独立重验 | 新 config_version/set;对应 runtime 独立置 false;审计/outbox 同事务 | 不沿用旧 verified 证据;不暗改 enabled/default/priority;不调用真实上游 | 每参数 `response.json`、`db-diff.json`、`external-calls.json` | +| `TDD-WP3-MDL-003-runtime-recommendation` | GEN-15、ADMIN-03、17.2、AC-30、51 | 三模型 verified/available,配置快照和哈希已记录 | 1. 依次模拟默认模型不可用、全部不可用、一个原因恢复但另一原因仍在、全部恢复;2. 每步 GET models | configured default 始终原 ID;recommended 按可用模型 priority 派生或 null;reason 使用固定优先级 | 只更新 `model_runtime_availability`/GatewayBalanceState/runtime version;config set/version/hash 不变 | 不写 enabled/is_default/priority;不自动恢复;不把推荐变成第二默认或用户隐式选择 | 每步 `response.json`、`db-diff.json`、`screenshots/model-states.png` | + +#### 6.9.8 自动保存与冲突 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP2-PROJ-002-debounce` | PROJECT-04、06、AC-20 | saved project version N、可控前端时钟 | 1. 1 秒内完成多次已提交编辑;2. 在首个保存请求在途时继续编辑;3. 释放响应并推进时钟;4. 撤销/重做 | 同时最多一请求;第一份后下一请求为合并后的最新完整状态;状态 saving→saved | 每次成功 state_version +1;最终 project_states 为最新已提交 JSON;撤销/重做也正常保存 | 不逐操作并发;不保存 Fabric 私有字段;不把未提交编辑或撤销栈写浏览器持久存储 | `network-timeline.json`、`response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP2-PROJ-002-cas-success` | PROJECT-04、17.3、AC-20 | owner project version N,完整合法 Canvas JSON | 1. PUT state with `If-Match: N`;2. GET project;3. 用相同幂等请求重放 | 首次成功并返回 saved/N+1;重放返回第一次结果 | `UPDATE ... state_version=N` 命中一行;project_states 新快照,state_version 仅 +1 | 不隐式上传/复制/派生资源;不泄漏或接受他人资源引用;不因重放 +2 | `request.json`、`response.json`、`db-diff.json` | +| `TDD-WP2-PROJ-004-stale-tab` | PROJECT-07、AC-36 P0-A 子集 | `FX-PROJECT-CONFLICT`,标签 A/B 均基于 N | 1. A 保存到 N+1;2. B 以 N 保存;3. B 尝试编辑/覆盖/另存副本 | B 返回 412 和最新安全版本摘要;立即只读 conflicted,只显示一次本地导出与刷新 | 仅 A 状态写入;B 的 state_version/save_status/latest_exports/project 数量不变 | B 不自动重试覆盖、不 merge、不另存项目、不恢复自动保存 | 两标签 `response.json`、`db-diff.json`、`trace.zip`、`screenshots/conflicted.png` | +| `TDD-WP2-PROJ-002-save-failure-recovery` | PROJECT-04、05、EXPORT-06、AC-20、35 | saved N;页面创建 dirty;参数化网络失败、storage unavailable、强制刷新/崩溃 | 1. 触发保存失败;2. 检查 failed/离开弹层;3. 页面保持时恢复并重试;4. 另一路强制退出后重开;5. 枚举浏览器存储 | 失败不显示 saved;正常恢复可保存;强制退出只恢复后端 N 且撤销栈为空 | 失败期间 DB 不变;成功重试才 N+1;浏览器持久存储无草稿/提示词/私有引用 | 不把失败当成功;不在关闭后恢复内存草稿;不让 SSE 代替 REST 真相 | `response.json`、`db-diff.json`、`cache-enumeration.json`、`trace.zip`、`screenshots/save-failed.png` | + +#### 6.9.9 导出 + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP4-EXP-001-confirm-pending-edit` | PROJECT-04、06、EXPORT-03、AC-23 | saved 画布、一次未提交文字/样式编辑、撤销栈深度 U | 1. 打开导出;2. 看到“导出前需要提交当前修改”;3. 确认;4. 等待画布稳定并启动正常自动保存;5. 合成导出;6. 执行一次撤销 | 导出使用确认后的状态;确认使撤销栈 U→U+1 且撤销可回到确认前;导出动作不再增加撤销项 | 确认后的完整状态进入正常保存队列;保存成功按 PROJECT-04 增加 state_version;latest 是否更新由后续保存结果决定 | 不在确认时产生多条撤销;不以导出额外编辑画布;不导出确认前状态;不跳过自动保存流程 | `undo-trace.json`、`network-timeline.json`、`export-hash.json`、`trace.zip`、`screenshots/pending-confirm.png` | +| `TDD-WP4-EXP-001-cancel-pending-edit` | EXPORT-03、AC-23 | 与上一 case 相同 | 1. 打开弹层;2. 取消未提交修改确认;3. 检查画布/撤销/网络 | 弹层关闭或停留在未导出状态;无下载、无成功提示 | state_version、latest_exports、撤销栈、保存队列均不因取消改变 | 不提交当前编辑、不合成、不下载、不调 `EXPORT_SAVE` | `network-timeline.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP4-EXP-001-format-ratio` | EXPORT-01 至 03、AC-23 | `FX-CANVAS-RATIOS`,JPG quality 参数化 80/92/100,PNG | 1. 对四比例分别导出 JPG/PNG;2. 解码像素/ICC;3. 与 Canvas fixture 比较 | 尺寸精确为 1080×1440、1080×1080、1440×1080、1080×1920;sRGB、无水印、PNG 无损、JPG 范围/默认正确 | 纯客户端合成本身无 DB 变化;后续保存只处理同一 Blob | 不缩放到其他尺寸;PNG 时质量控件不可聚焦;不因导出改变画布/撤销栈 | 每组合 `result.json`、`image-metadata.json`、`pixel-diff.json`、`export-hash.json` | +| `TDD-WP2-EXP-001-same-blob-success` | EXPORT-04、AC-23、35 | saved project,已有旧 JPG latest;新客户端 Blob B | 1. 记录 B SHA-256;2. 用 B 触发浏览器下载;3. 同一 B 调 `EXPORT_SAVE`;4. 重读 latest 并下载 | UI 显示“已下载并保存为最新成品”;两个通道 hash 相同 | 原子替换该格式 latest_exports/managed file;旧 JPG 进入规定清理;PNG latest 不变;容量按真实文件更新 | 不重新编码第二份 Blob;不保留完整导出历史;不影响另一格式;幂等重试不重复计量 | `download.json`、`request.json`、`response.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | +| `TDD-WP2-EXP-001-result-matrix` | EXPORT-05、06、AC-35、55 | 参数化:合成失败、下载失败、project 保存失败、latest 持久化失败、storage full/unavailable | 1. 在 UIDesign 四结果组合及存储状态下导出;2. 记录下载/上传调用顺序;3. 重读项目/latest | 分别显示冻结结果文案;可合成时允许规定本地下载;下载失败可复用同一次合成;后端失败明确 latest 未更新 | 失败分支保留旧 latest;project 保存失败不写新 state;latest 失败不写 latest/managed file;成功分支才更新 | 不撤回已下载文件;不以本地下载覆盖后端记录;不显示双成功假状态;不把 Blob 存浏览器持久缓存 | 每参数 `network-timeline.json`、`response.json`、`db-diff.json`、`trace.zip`、`screenshots/result.png` | +| `TDD-WP2-PROJ-004-conflict-export-once` | PROJECT-07、EXPORT-05、AC-36 P0-A 子集 | conflicted 标签页,`conflict_export_used=false` | 1. 本地导出一次(成功和失败各参数);2. 再次尝试;3. 刷新最新版本 | 第一次结束后立即禁用再次导出;结果明确“仅下载本页版本,未写入项目”;刷新到后端最新状态 | 所有步骤不调用 latest API,projects/project_states/latest_exports 无变化;flag 只在页面内存 | 不因第一次失败允许第二次;不覆盖后端、不另存副本、不把 flag 持久化 | `network-timeline.json`、`db-diff.json`、`trace.zip`、`screenshots/conflict-export.png` | + +#### 6.9.10 WP-0 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP0-DEP-001-frozen-toolchain` | NFR-09、DevelopmentPlan 2.1、13.1 | 冻结 lockfile、干净离线依赖缓存、win-x64 runner | 1. 严格按 lockfile 安装;2. 编译 TypeScript;3. 最小加载 React/Fabric/Fastify/better-sqlite3;4. 用随包 Node 运行 native smoke | 所有命令退出 0,版本与 lockfile/DevelopmentPlan 一致 | 只生成声明的构建缓存与报告;业务 DB/LocalDataRoot 无变化 | 不升级/替换依赖、技术栈或 native 模块;不联网漂移解析版本 | `commands.json`、`dependency-tree.json`、`native-smoke.json` | +| `TDD-WP0-API-001-schema-envelope` | NFR-05、DevelopmentPlan 6.1、6.6 | 生成 TypeBox/OpenAPI/客户端 snapshot;合法与非法 fixture | 1. 生成 OpenAPI 3.1;2. 比较 snapshot;3. 对每个 operation 注入合法/非法请求;4. 校验错误信封和 correlation ID | Schema/运行/客户端一致;非法响应使用冻结 HTTP/code/message_key/details 白名单 | 只读场景 DB 无变化;写场景失败按各 operation 事务回滚 | 不出现未声明字段/枚举、供应商原文、堆栈、路径或缺失 correlation ID | `openapi.json`、`snapshot-diff.json`、`response.json`、`redaction.json` | +| `TDD-WP0-BND-001-loopback-origin` | NFR-09、AC-24 | API 未启动;回环、局域网、错误 Host/Origin 和预检请求参数 | 1. 启动产品包;2. 检查监听地址;3. 从允许/拒绝地址请求;4. 发送跨源/CORS 预检 | 只接受 `127.0.0.1:43121` 与同源;其他路径连接失败或按门禁拒绝 | 无业务表、会话、外部用量变化 | 不监听 `0.0.0.0`/IPv6 wildcard,不建防火墙入站规则,不返回 CORS 放行头 | `socket-listeners.json`、`response.json`、`firewall-diff.json` | +| `TDD-WP0-BRW-001-real-supported` | NFR-01、AC-24、41 | 最终 `RELEASE.json`;真实 Chrome/Edge 独立 profile | 1. 分别启动记录的 full version;2. 完成 UA-CH support check;3. 进入 bootstrap/login;4. 复查产品 API | 两浏览器均通过并建立短期支持 cookie,产品 API 每次复核品牌/major | 仅门禁所需非敏感记录/cookie;不建用户 session | 不用 Playwright Chromium、伪 UA 或前一 major 代替;不联网查询当前版本 | 每浏览器 `environment.json`、`response.json`、`trace.zip`、`screenshots/supported.png` | +| `TDD-WP0-BRW-002-hard-block` | NFR-01、AC-24 | 参数化其他品牌、错误 major、非 Windows、UA-CH 缺失/冲突/解析失败和原始 API 客户端 | 1. 请求 support check;2. 请求 HTML/bundle/API;3. 尝试登录/项目/后台深链;4. 查 DB/外部调用 | 全部 426 `BROWSER_UNSUPPORTED`,reason 仅四类,页面只有重新打开说明且无绕过 | 业务 DB 读取/写入计数为 0,session/验证码/资源读取均无变化 | 不返回继续令牌/参数,不发 Resend/AI/高德,不泄露产品数据 | 每参数 `response.json`、`db-access.json`、`external-calls.json`、`screenshots/blocked.png` | +| `TDD-WP0-SUP-001-lifecycle` | NFR-09、AC-24、41 | 真实便携包;参数化第二实例、43121 占用、API/Worker 崩溃、正常退出 | 1. 双击启动;2. 注入各故障;3. 观察托盘/重启次数;4. 退出并等待 15 秒 | 单实例;端口占用明确失败且不换端口;有限重启/degraded/允许动作符合契约;退出后无残留 | 仅规定日志/状态记录;正常退出 flush WAL/队列;不改业务真相 | 不无限重启、不杀无关进程、不弹隐藏控制台、不留下 API/Worker/端口 | `process-tree.json`、`port.json`、`supervisor-events.json`、`screenshots/system-ui.png` | +| `TDD-WP0-DATA-001-root-validation` | NFR-09、17.19、AC-41、55、56 | 参数化 repo/project/download/只读素材根、合规外部目录、已初始化后缺失目录 | 1. 用离线配置设置各路径;2. 启动;3. 删除已初始化根后重启;4. 调资源 API | 违规路径拒绝;合规路径可写;缺失根进入 data_missing 且不自动重建;浏览器只见逻辑资源 ID | 仅合规首次初始化创建固定目录/DB;失败不创建散落目录 | 不硬编码用户名、不回显绝对路径、不在源码/下载/素材根写业务数据 | `config-result.json`、`fs-before.json`、`fs-after.json`、`response.json` | +| `TDD-WP0-DATA-002-no-backup-migration` | AUTH-06、NFR-09、AC-24、56 | 有账号/项目/下载成品的 LocalDataRoot | 1. 搜索 UI/API/包功能;2. 尝试导出项目包/DB/账号/点数;3. 删除根/重装;4. 启动模拟 P0-B 导入 | 仅原图/JPG/PNG 下载可用;固定本机提示持续显示;删除/重装不可恢复;无迁移入口 | 下载目录文件保留;业务数据删除后不被重建或导入 | 不自动备份、不生成迁移包、不把下载图识别为正式迁移数据 | `route-inventory.json`、`package-scan.json`、`trace.zip`、`screenshots/fixed-copy.png` | +| `TDD-WP0-STO-001-byte-thresholds` | 17.19、AC-55 | 第 5.5 节七个精确字节 fixture,可写目录 | 1. 对每个值重计量;2. 读取 storage state/API/UI;3. 重复计算 | normal/warning/critical/full 边界逐字节正确,重复结果确定 | 只更新 measured bytes/notice/status/last_measured_at;无 managed file 变化 | 不用浮点比例导致错一字节,不自动删除,不把 warning/critical 当阻断 | 每值 `result.json`、`db-diff.json`、`screenshots/capacity-bar.png` | +| `TDD-WP0-FILE-001-atomic-commit` | NFR-05、17.19、AC-44、55 | 受控文件驱动;合法/错误 MIME、hash、`..`、符号链接逃逸;各崩溃点 | 1. 流式写 staging;2. 校验/预留;3. 在改名/DB 提交前后终止;4. 重启对账 | 非法输入拒绝;成功对象完整可读;崩溃后恢复为已提交或补偿清理,不出现半对象 | reservation、managed_files、staging/正式文件按提交点一致;启动对账修复计量 | 不接受扩展名猜 MIME、不越根、不产生可读半文件/孤儿引用/重复计量 | `response.json`、`crash-points.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | +| `TDD-WP0-CACHE-001-public-lru` | NFR-07、17.14、AC-46、48 | 公开资源超过 157,286,400 字节;internal/private/project/提示词 fixture | 1. 按访问顺序填充 public cache;2. 触发写前 LRU;3. 尝试缓存私有类型;4. 枚举 SW/Cache/IndexedDB | 总量不超上限,淘汰最久未用且当前资源可重取;私有类型均不缓存 | `public_asset_lru` 只含公开元数据;业务 DB 无变化 | 不缓存 internal preview、私有内容、项目、验证码、session、未保存编辑;不计用户下载 | `cache-enumeration.json`、`lru-trace.json`、`trace.zip` | +| `TDD-WP0-EVT-001-rest-refetch` | PROJECT-04、GEN-14、NFR-05、DevelopmentPlan 6.2、AC-20、30 | 可控 SSE 序列:正常、断线、event_id 缺号、runtime/config 变化 | 1. 订阅 `/api/v1/events`;2. 注入事件;3. 断线/缺号;4. 观察 REST 请求和 UI | 事件只提示 ID/type/entity/version/time;每次由 REST 取真相;断线/缺号 bootstrap/refetch | SSE 不直接写业务表;REST 读取后前端状态更新 | 不携带正文/图片/凭据,不把 runtime 事件写 ModelConfig,不抢焦点 | `sse-events.json`、`network-timeline.json`、`trace.zip` | +| `TDD-WP0-SEC-001-credential-channel` | PRIV-01、AC-41 | Windows Credential Manager 测试条目;API/Worker 各需不同凭据;缺失/清除参数 | 1. Supervisor 读取;2. 通过对应匿名管道启动子进程;3. 检查命令行/环境/句柄;4. 清除并重启 | 仅目标子进程能在内存读取一次;缺失时服务明确不可用/启动失败 | 不写业务 DB;只写非敏感状态与日志 | 凭据不进参数、环境、文件、日志、崩溃诊断;API 不获网关密钥,Worker 不获 Resend/高德密钥 | `process-env-scan.json`、`pipe-acl.json`、`redaction.json` | +| `TDD-WP0-LOG-001-rotation-redaction` | PRIV-01、02、NFR-09、AC-41、56 | 生成超过 10 MiB/10 文件/30 天日志;注入密钥、邮箱、提示词、路径和写失败 | 1. 写边界日志;2. 触发轮转/保留清理;3. 复制诊断;4. 使目录不可写并尝试 AI | 轮转严格为 10 MiB/10 文件/30 天;诊断只含白名单;写失败进入 unavailable 并阻断 AI | 只更新日志文件和非敏感状态;不改业务对象 | 不记录敏感正文/绝对路径/供应商原文,不在日志失败时继续 AI | `log-manifest.json`、`redaction.json`、`response.json`、`external-calls.json` | +| `TDD-WP0-PKG-001-portable-zip` | NFR-01、09、AC-24、41、56 | release build、空白 Windows 用户目录 | 1. 生成 win-x64 ZIP/哈希/START-HERE/RELEASE;2. 解压非仓库目录;3. 离线启动;4. 扫描制品和端口 | 包含 Dada.exe、随包 Node/native 依赖并固定 43121;说明与 RELEASE 一致 | 仅合规 LocalDataRoot 生成运行数据;制品本身不可变 | 不含真实配置/凭据/用户名/LocalDataRoot,不动态换端口,不要求 Docker/安装器 | `package-manifest.json`、`sha256.txt`、`package-scan.json`、`process-tree.json` | + +#### 6.9.11 WP-1 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP1-NOTICE-001-registration-consent` | AUTH-03、PRIV-04、06、AC-01、41、56 | 当前告知 version/effective_at/hash;缺字段、未勾选和完整参数 | 1. 展开注册第二阶段;2. 检查全部处理方/用途/本机风险;3. 未同意提交;4. 同意当前 version 注册 | 未同意/版本错误不能注册;完整告知原样可达;成功进入创作 | 失败无 user/consent/invite/credit;成功记录 consent version/time 与账号同事务 | 不省略 AI/Resend/高德、DYN004、管理员访问、注销、180 天、无备份/迁移;不预勾选 | `response.json`、`db-diff.json`、`screenshots/notice-expanded.png` | +| `TDD-WP1-SLOT-001-stage-limit` | AUTH-01、NFR-04、AC-32、45 | 参数化 9/10 个 active+suspended 普通账号、deleted、多个 super_admin | 1. 发码前尝试第 10/11 人;2. 在发码后并发占满名额再完成;3. 删除普通账号再注册 | 第 10 人可成功,第 11 人发码/完成均阻断;deleted 释放;super_admin 不占名额 | 成功时计数/账号/邀请原子;失败零变化;累计统计保留 deleted | 不把 suspended/deleted/super_admin 计错,不通过并发超 10,不让失败占邀请码 | `response.json`、`db-diff.json`、`concurrency-trace.json` | +| `TDD-WP1-ADM-001-admin-auth-boundary` | AUTH-07、AC-49 | 白名单/非白名单邮箱、已存在普通账号冲突、两个 admin、全部停用状态 | 1. 后台发码;2. 首次验证/后续登录;3. 尝试普通 session 访问后台;4. 停用全部并尝试自助恢复 | 非白名单发码前拒绝;白名单首次建 super_admin;audience 严格分离;无最后管理员保护且无自助恢复 | 允许分支建 admin/session/审计;拒绝分支不建 challenge/session;停用撤销 session | 不泄漏白名单、不使用邀请码/普通名额、不原地提升普通账号、不创建部署人员角色 | 每参数 `response.json`、`db-diff.json`、`external-calls.json`、`trace.zip` | +| `TDD-WP1-CFG-001-secure-revision` | AUTH-07、ADMIN-09、AC-49、50 | current applied revision N;参数化完整 N+1、HMAC/凭据缺失、身份冲突 | 1. 离线写候选安全配置;2. API 启动应用;3. 查询 apply state/session;4. 注入失败重试 | 完整配置原子生效并推进 revision;失败保持 N 并阻止受影响入口 | 成功同事务更新 apply state、撤销受影响 session、写 system/backend_secure_config 审计;失败全回滚 | 不写白名单/HMAC/pepper/人员身份/凭据,不出现半生效配置 | `config-result.json`、`db-diff.json`、`redaction.json` | +| `TDD-WP1-DEL-001-account-delete` | AUTH-06、AC-22、39、56 | 用户拥有项目、参考/生成/导出、坐标、点数、多个 session | 1. 确认注销;2. 并发调用受保护 API;3. 尝试旧资源 ID/恢复;4. 用新邀请重新注册同邮箱 | 注销成功后所有旧访问立即 401/404;不可恢复;新注册是全新账号 | 同事务撤销 sessions、删除身份/业务关系/未用点数并创建允许匿名事件;物理文件排队 | 不延迟撤权到物理清理,不恢复旧 ID/点数/资料,不保留邮箱映射到匿名主体 | `response.json`、`db-diff.json`、`worker-events.json`、`trace.zip` | +| `TDD-WP1-DEL-002-anonymous-retention` | AUTH-06、17.10、17.11、AC-22、39 | 注销前有生成/点数事件和私有访问审计;可控 180 天时钟 | 1. 注销;2. 检查匿名字段;3. 在到期前尝试管理删除;4. 到期运行 Worker | 匿名事件只含允许统计;访问审计目标不可反推;到期前不可删,到期后系统清理 | 原身份关联移除;随机匿名主体无映射;expires_at 不因注销延长;到期行删除 | 不保留邮箱/IP/提示词/图片/项目/job 原 ID,不让管理员手删或延长期限 | `db-diff.json`、`retention-events.json`、`redaction.json` | +| `TDD-WP1-AUD-001-immutable-triggers` | ADMIN-05、09、17.10、17.16、AC-50 | 各一条 AdminOperationLog/PrivateContentAccessLog;普通、retention、privacy purge 连接 | 1. 普通连接 UPDATE/DELETE;2. 伪造 purge 函数;3. 合法作用域在到期/注销执行 | 普通和伪造操作被 trigger 阻断;仅注册作用域执行规定清理 | 非法尝试零变化;合法作用域只删除/匿名化允许行并留非敏感结果 | 不允许后台/API 任意修改,不混用两类日志,不把正文写审计 | `sql-results.json`、`db-diff.json`、`trigger-definition.json` | + +#### 6.9.12 WP-2 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP2-PROJ-001-new-versus-continue` | PROJECT-01 至 03、GEN-04、11、AC-03、06、34 | `FX-PROJECT-ACTIVE-19`、一个 9 图固定比例项目 | 1. 工作台新建两次;2. 在第一个项目继续生成;3. 尝试改比例;4. 补到 10 图再继续 | 两次新建为两个 project;继续只新增 job/history且沿用比例;10 图阻断追加但可另建 | projects +2;继续分支 project 数不变;成功 history 正确关联 | 不默认追加最近项目、不创建第三项目、不改变既有项目比例、不让历史上限阻断其他新建 | `response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP2-PROJ-003-trash-restore-purge` | PROJECT-03、08、AC-21 | active project、`FX-PROJECT-ACTIVE-20`、可控 720 小时时钟 | 1. 移入回收站;2. 满 20 时恢复;3. 释放名额再恢复;4. 主动永久删除;5. 另一项目推进到 purge_at | 满额恢复阻断且时间不变;释放后可恢复;purge 后立即不可见/不可恢复 | status/deleted_at/purge_at 精确;purged 撤销访问关系并排物理清理 | 不提前物理结果、不因恢复重算 purge_at、不让 purged 资源可读 | `response.json`、`db-diff.json`、`worker-events.json` | +| `TDD-WP2-PROJ-005-failed-draft-retry` | PROJECT-01、GEN-09、AC-04、43 | `FX-PROJECT-EMPTY` 和含成功图项目 | 1. 失败页重试两次;2. 返回工作台主动新建;3. 多选批量删除 failed_empty 与成功项目 | 重试只在原 project 新增 job;主动新建才新 project;批量只接受无成功图草稿 | 原草稿保留 prompt/config;job 数递增;批量合格项 trashed,成功项目不变 | 不把重试变新项目、不把成功项目纳入快捷批量、不丢草稿输入 | `response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP2-REF-001-reference-lifecycle` | GEN-05、PROJECT-09、AC-44、48 | 新上传、同项目已有 reference、他人/跨项目/撤销/已 purge reference | 1. 提交前在页面选择/移除;2. multipart 新上传;3. 同项目复用;4. 尝试越权/失效;5. 删除项目 | 提交前仅内存;每 job 独立关系快照;仅同 owner+project 可复用;purge 后立即不可读 | 成功建立 job reference/managed file;移除前无文件;purge 撤销关系并排清理 | 不修改旧任务关系、不跨用户/项目复用、不缓存预提交图片、不暴露路径 | 每参数 `response.json`、`db-diff.json`、`fs-after.json`、`cache-enumeration.json` | +| `TDD-WP2-EXP-002-empty-latest` | EXPORT-04、AC-23、38 P0-A 子集 | 从未导出但有成功原图的项目 | 1. 打开项目详情/latest 区;2. 请求 JPG/PNG latest;3. 使用允许动作 | 显示“暂无导出成品”,只提供进入编辑并导出和下载原始生成图 | latest_exports 无行且不因读取创建;原图读取只读 | 不自动合成/保存默认成品,不伪造跨设备下载,不改变项目 | `response.json`、`db-diff.json`、`trace.zip`、`screenshots/no-export.png` | +| `TDD-WP2-BAL-001-balance-impact` | GEN-14、15、17.17、AC-04、51 | 参数化 model/account/unknown 影响;活动任务与空闲模型 | 1. 注入余额不足;2. 结算已提交任务;3. GET models/后台;4. 重复事件;5. 未确认恢复前尝试启用 | 影响范围按规则;任务 failed+release 一次;runtime 不可用;恢复前操作阻断 | GatewayBalanceState/affected models/runtime/审计更新;ModelConfig hash 不变 | 不自动充值、不用 Dada 点数继续调用、不重复 release、不自动恢复或改默认/priority | `response.json`、`db-diff.json`、`worker-events.json`、`external-calls.json` | + +#### 6.9.13 WP-3 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP3-ADP-001-per-model-contract` | GEN-01、02、05、06、13、16、AC-40 | 三个模型各自独立 mock/真实受控账号和脱敏 fixture | 1. 每模型分别执行纯文字、合法参考图、四比例;2. 覆盖同步/异步与查询;3. 验证 MIME/尺寸/单图;4. 注入九类映射 | 每模型只按自身已证实协议成功;结果恰一图;错误映射到固定 category | 真实验证只写对应 model/config_version 的 evidence 状态;任务/点数按独立用例结算 | 不从一个模型推断另两个、不共享 verified 标记、不接受多图/未知字段、不记录原始响应 | 每模型 `contract-matrix.json`、`response.json`、`external-calls.json`、`redaction.json` | +| `TDD-WP3-ADP-002-invalidate-revalidate` | GEN-13、15、ADMIN-03、AC-30、40 | 三模型 verified;参数化端点/字段/参考图/响应/异步/查询/error protocol 变化 | 1. 改一个契约字段并保存;2. 尝试生成;3. 仅为该模型重跑完整矩阵;4. 再尝试 | 变化后立即 unverified/不可新建;完整独立重验后才恢复 verified | 新 config_version;对应 runtime/evidence 版本更新;其他模型证据不变 | 不沿用旧证据、不局部 smoke 即 verified、不暗改 enabled/default/priority | 每字段 `db-diff.json`、`contract-matrix.json`、`response.json` | +| `TDD-WP3-BAL-001-confirmed-recovery` | GEN-14、15、ADMIN-09、AC-51 | 余额导致 model/account/unknown runtime unavailable;无/有真实恢复确认 | 1. 未确认时点击恢复;2. 执行非敏感健康检查;3. admin 明确确认;4. 重新计算所有阻断原因 | 无确认失败;确认真实恢复后只解除已满足原因,仍有其他原因则继续 unavailable | recovery check、GatewayBalanceState/runtime/AdminOperationLog 同事务更新;config set 不变 | 不自动恢复/充值,不补扣用户点数,不清除仍存在的 contract/Worker 阻断 | `response.json`、`db-diff.json`、`external-calls.json`、`screenshots/recovery.png` | + +#### 6.9.14 WP-4 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP4-BG-001-switch-background` | GEN-12、EDITOR-01、PROJECT-04、AC-07 | 画布含文字/贴纸/色卡/动态元素和已调底图,项目有第二历史图 | 1. 记录覆盖元素;2. 确认换底图;3. 检查参数/色卡;4. 撤销/重做;5. 重开 | 覆盖层位置尺寸保留;底图处理重置;五色重提;一次撤销且重开一致 | 完整 Canvas state 自动保存并 state_version 递增 | 不删除/缩放覆盖层,不保留旧调色,不产生多次撤销或改项目比例 | `canvas-before.json`、`canvas-after.json`、`db-diff.json`、`trace.zip`、`pixel-diff.json` | +| `TDD-WP4-BG-002-processing-controls` | EDITOR-01 至 03、PROJECT-04、EXPORT-03、AC-07、20、23 | 固定底图和像素基线 | 1. 参数化填充/适配/裁剪/缩放/定位及亮度、对比度、饱和度、色温、锐度、预设滤镜;2. 完成;3. 撤销;4. 恢复原图;5. 导出/重开 | 每项预览与提交值一致;完成一次撤销;导出/重开一致;恢复原图只清底图处理 | 每次提交进入保存的 Canvas JSON;取消无 DB 变化 | 不影响覆盖元素,不持久化未提交预览,不用静默超范围值 | 每参数 `canvas-state.json`、`db-diff.json`、`pixel-diff.json`、`trace.zip` | +| `TDD-WP4-CAN-001-fifty-elements` | EDITOR-04 至 09、17.5、AC-27 | `FX-CANVAS-50`,稳定选择重叠 fixture | 1. 添加到 50;2. 循环选择/长按候选/框选/多选移动;3. 层级/复制/删除;4. 保存/导出;5. 添加第 51 | 前 50 可编辑保存导出;选择顺序确定;第 51 明确阻断且布局不跳 | Canvas 最多 50,成功操作各一次撤销/保存;第 51 无 state change | 不出现图层面板/隐藏/锁定/编组,不因阻断损坏已有元素 | `canvas-state.json`、`db-diff.json`、`trace.zip`、`performance.json` | +| `TDD-WP4-TXT-001-multiline-template-switch` | TEXT-01 至 06、PROJECT-04、AC-09、10 | 多行文字模板实例、固定变换 | 1. 输入含换行文字并完成;2. 切换另一模板;3. 撤销/重做;4. 重开/导出 | 换行持久;空内容按规则拒绝;切模板重置默认样式但保留位置/旋转/整体尺寸 | 每次完成一条撤销并保存最新 element snapshot | 不把切模板重置变换、不裁切内容、不保存取消编辑 | `canvas-state.json`、`db-diff.json`、`pixel-diff.json`、`trace.zip` | +| `TDD-WP4-TXT-002-font-metrics-ranges` | TEXT-07 至 14、NFR-02、AC-11、12 | 真实归档字体/缺失字体、最长文字、属性边界/步长 | 1. 等待 FontFace ready;2. 参数化字号/缩放/行距/字距/描边/颜色/背景透明度;3. 完成;4. 缺字体加载;5. 重开/导出 | 字号与控制柄同步,重新测量不裁切;范围步长精确;缺字体模板 unavailable;背景透明度不影响文字 | 完成一次撤销/保存;缺字体分支无 Canvas 新元素 | 不静默系统字体回退、不把背景透明度应用文字、不超范围取值 | `font-load.json`、`canvas-state.json`、`pixel-diff.json`、`trace.zip` | +| `TDD-WP4-TXT-003-catalog-search-recent` | TEXT-01、17.6、AC-09、32 | P0-A 32 文字模板、四分类、稳定 catalog、两个用户 | 1. 未选模板尝试加文字;2. 按显示名称搜索;3. 检查分类/顺序;4. 使用模板;5. 切换账号读最近使用 | 只能从模板添加;四类/显示名搜索/稳定顺序正确;最近使用按账号隔离 | 只在成功使用后更新 `recent_assets`;未选/搜索不写项目 | 不提供独立普通文字,不复用普通贴纸搜索,不跨账号共享最近项 | `catalog.json`、`response.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP4-STK-001-transform-sticker` | EDITOR-04 至 09、PROJECT-04、AC-13、27 | 普通贴纸元素和重叠选择 fixture | 1. 移动/缩放/旋转/翻转/透明度;2. 复制/层级/多选;3. 撤销重做;4. 保存/导出重开 | 画布、属性控件、导出和重开状态一致;每次连续手势是一项撤销 | Canvas element snapshot/state_version 按提交更新 | 不改变 stable ID/resource_version,不引入隐藏/锁定/编组,不丢选择 | `canvas-state.json`、`db-diff.json`、`pixel-diff.json`、`trace.zip` | +| `TDD-WP4-COL-001-deterministic-palette` | COL-01 至 05、AC-15 P0-A 子集、16 | 固定解码底图、`mmcq-v1`、四个 P0-A 色卡 | 1. 重复提取五色;2. 调裁剪/调色/滤镜;3. 换原始底图;4. 渲染四布局;5. 监控网络 | 同图五色/顺序确定;处理不刷新;换底图刷新;四色卡消费同一五色 | 五色快照只在底图更换提交时保存;无上传文件 | 不读取处理后像素/额外图片,不联网提色,不启用白名单外色卡 | `palette.json`、`network-timeline.json`、`pixel-diff.json`、`db-diff.json` | +| `TDD-WP4-DYN-001-fixed-time` | DYN-01 至 03、AC-14 | 可控时钟、DYN001-003/007/008/011 fixture | 1. 插入/确认动态值;2. 推进墙钟;3. 重开;4. 导出 | 使用插入/确认时快照,重开/导出不随当前时间变化;结果静态不可点击 | 动态实例只保存允许字段和值快照 | 不保存定时器/外部链接,不在重开重新计算,不输出 GIF/视频 | `canvas-state.json`、`clock-trace.json`、`pixel-diff.json` | +| `TDD-WP4-DYN-002-location-consent` | DYN-04、PRIV-03、AC-17、47 | DYN004 固定坐标/高德 mock;同意、拒绝、服务暂停参数 | 1. 插入前检查 Dada 提示;2. 拒绝;3. 同意并定位/逆地理;4. 手动地点回退;5. 删除元素/注销 | 拒绝调用 0 且不插入坐标;同意后正确显示;暂停时自动定位禁用但手动地点可用 | 只在同意后把坐标/文本存实例;删除/注销按规则清理 | 前端不持高德密钥,不把坐标用于其他元素/用户,不在提示前调用 | `external-calls.json`、`canvas-state.json`、`db-diff.json`、`trace.zip` | +| `TDD-WP4-DYN-003-identity-font` | DYN-05 至 09、AC-18、19 | 用户社交 ID 含/不含 `@`;实例覆盖;DYN012/FONT081 | 1. 插入身份贴纸;2. 修改实例覆盖;3. 检查账号资料;4. 插入 DYN012;5. 重开/导出 | 最多一个前导 @;实例覆盖独立且不改账号;DYN012 使用 FONT081 并明确标注替代 | 只保存实例 snapshot;UserProfile 不变 | 不声称 DIN 字体、不静默回退、不执行 Lua/Prefab、不把覆盖写账号 | `canvas-state.json`、`db-diff.json`、`font-load.json`、`pixel-diff.json` | +| `TDD-WP4-A11Y-001-keyboard-focus` | NFR-06、AC-32 | 第 11.1 节全部 page_id,最长文案、弹层和 200% 缩放 | 1. 仅键盘走主流程;2. 检查 skip link/focus order/弹层返回;3. 触发 live region;4. 运行 axe;5. 测画布等价控件 | 所有动作可达,焦点不逃逸/不被 SSE 抢走;critical/serious 为 0;状态非仅颜色 | 无非用户触发业务写;可访问性扫描只产证据 | 不用正 tabindex、不隐藏主动作、不以缩小字体规避 200% 问题 | 每页 `axe.json`、`focus-trace.json`、`trace.zip`、`screenshots/200pct.png` | +| `TDD-WP4-VIS-001-browser-diff` | NFR-02、AC-19、23、32 | 最终 RELEASE Chrome/Edge、固定 Canvas/字体/动态值/DPR | 1. 等待资源稳定;2. 两浏览器截图/导出;3. 计算元素边界/显著像素;4. 人工复核已知替代 | 结构、字体、换行、颜色、描边、装饰在第 10.1 阈值内;替代项明确标记 | 只生成视觉证据;业务状态使用同一 fixture | 不用低保真 PNG 作像素基准,不用阈值掩盖缺字体/空画布,不冒充全 P0-B | 每场景 `pixel-diff.json`、`layout-boxes.json`、`manual-review.json`、截图 | +| `TDD-WP4-PERF-001-budget` | NFR-03、AC-27、32 | `FX-CANVAS-50`、1,407 贴纸目录、四比例导出、最终浏览器 | 1. 预热一次;2. 每预算运行五次;3. 记录 p50/p95/max/memory/DOM;4. 注入导出失败 | 第 10.2 节所有预算通过;失败不破坏 project/latest;资源面板节点不线性增长 | 只生成性能数据;失败分支 DB/latest 不变 | 不提高超时、减少元素/素材或降低尺寸掩盖失败,不用 Chromium 替代真实浏览器 | `performance.json`、`memory.json`、`dom-count.json`、`environment.json` | + +#### 6.9.15 WP-5 全部测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP5-CAT-001-catalog-1407` | STATIC-01 至 04、AC-13、32 | 规范 catalog/metadata,含 3 组重复图和 part1-25 | 1. 编译/加载目录;2. 校验 ID/part/顺序/计数;3. 滚动全目录;4. 观察原图请求/DOM | 恰 1,407 项,重复图片保留不同稳定 ID;虚拟列表稳定,原图按需加载 | 只读 catalog;不复制或改写源;浏览器仅缓存允许公开项 | 不去重业务 ID、不一次下载全量、不暴露绝对路径、不线性增长 DOM | `catalog-validation.json`、`network-timeline.json`、`dom-count.json`、`source-hashes.json` | +| `TDD-WP5-MAN-001-readonly-compiler` | TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、STATIC-01 至 STATIC-04、AC-32、42 | 只读规范入口与独立输出目录;Lua/Prefab/证据目录 trap | 1. 记录源 hash/mtime;2. 运行声明式转换;3. 注入尝试执行 Lua/Prefab/读取证据目录;4. 复核输出 | 只消费允许 catalog/metadata/资源;非法入口失败;输出 manifest 可重现 | 只写版本化派生输出;源 hash/mtime 零变化 | 不执行 Android runtime、不扫描证据目录、不复制约 3.57 GB 全库、不覆盖源 | `compiler-report.json`、`source-before.json`、`source-after.json`、`output-manifest.json` | +| `TDD-WP5-WHITE-001-p0a-allowlist` | TEXT-01、TEXT-04、COL-01、DYN-01、DYN-08、STATIC-01、ADMIN-07、NFR-02、NFR-07、AC-32、42、48 | 普通用户无 grant、预览用户有单批 grant、注册全量模板 | 1. 分别读取公开/预览 manifest;2. 搜索白名单外 ID;3. 浏览所有面板 | 普通用户仅 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 贴纸;预览只多授权批次 | 只读;授权不改变 role | 不向普通用户暴露白名单外 passed/internal,不提前开放 P0-B,不遗漏白名单;AC-54 为未来 P0-B,不进入本 case 的当前验收或 passed 关联 | `manifest.json`、`response.json`、`trace.zip`、`screenshots/allowlist.png` | +| `TDD-WP5-COL-001-four-layouts` | COL-01 至 05、AC-15 P0-A 子集、16、32 | COLOR001/002/008/016 与同一五色 fixture | 1. 渲染四卡;2. 改裁剪/滤镜;3. 换底图;4. 尝试访问其他色卡 | 四布局消费完全相同五色;刷新规则一致;其他色卡不可发现 | 只保存稳定 ID/version/五色 snapshot | 不启用其余 12 色卡、不各自重新提色、不联网读取图片 | `palette.json`、`pixel-diff.json`、`manifest.json` | +| `TDD-WP5-RES-001-three-access-classes` | PRIV-02、05、17.12、13、AC-42、48 | public、internal_preview、private 资源及不同主体/grant | 1. 请求三类 manifest/资源;2. 推导 stable ID/路径;3. 撤销 grant 后重取;4. 检查 headers/缓存 | public 可匿名于产品授权范围读取;preview 每次验 grant/no-store;private 仅所有者/受控 admin;越权 403/404 | 读取无业务写,访问审计仅私有完整内容规定入口产生 | 不混路由/cache header,不返回路径/internal manifest,不用公开 ID 推导私有对象 | 每主体 `response.json`、`headers.json`、`cache-enumeration.json` | +| `TDD-WP5-UPL-001-upload-metering` | ADMIN-06、17.7、17.19、AC-31、55 | PNG/WebP、坏 MIME/解码、稳定 ID 冲突;active/full/unavailable;原图+缩略图字节 | 1. multipart 上传并指定 part/order;2. 生成缩略图;3. 注入非法/冲突/容量状态;4. 重计量 | 合法上传成功并可上架;非法/冲突明确失败;full/unavailable 阻断写但已有资源可读 | 原图/缩略图分别 reservation/managed_file 并计量;失败零文件/目录项 | 不用扩展名替代解码、不漏计缩略、不部分覆盖稳定 ID、不在 full 写入 | `response.json`、`db-diff.json`、`fs-before.json`、`fs-after.json` | +| `TDD-WP5-CLN-001-reference-race` | ADMIN-06、09、17.7、17.19、AC-31、50、55 | 查询时整批文件全部无任何发布/项目/待发布引用 | 1. 查询候选并由 admin A 创建 intent;2. 参数化确认前新增各类引用/快照过期/admin 变化;3. A 确认;4. 无竞态分支确认并运行 Worker | 任一新增引用整批 409 conflict+denied 审计;stale 重新查询;无竞态才整批排队/物理删除/重测 | 冲突 bytes/状态/queue 不变;成功排队不减量,物理后同事务更新并记录全阶段审计 | 不让查询时有引用项入候选、不由 admin B 确认、不部分排队、不自动清理或提前减量 | 每参数 `response.json`、`db-diff.json`、`worker-events.json`、`fs-after.json` | +| `TDD-WP5-PREV-001-grant-lifecycle` | ADMIN-07、17.13、AC-42、48 | 普通账号,active/revoked/expired grant,已有项目引用预览版本 | 1. admin 授权批次;2. 用户读取/添加;3. 撤销/到期;4. 再读取/添加;5. 重开已有项目 | active 时只见授权项;撤销/到期立即阻断新读取/添加;已有项目按保存版本渲染 | grant 状态/审计更新;UserProfile.role 不变;项目引用版本不改写 | 不出现后台权限、不把 preview 写 public manifest/cache、不因撤销破坏既有项目 | `response.json`、`db-diff.json`、`cache-enumeration.json`、`trace.zip` | +| `TDD-WP5-CACHE-001-no-private-client-state` | NFR-07、17.14、AC-46、48 | 已访问 public/preview/private、提示词和项目;注册 SW fixture | 1. 完成各访问;2. 离线/刷新;3. 枚举 SW registrations、Cache keys/body、IndexedDB stores/records | 仅 public allowlist 元数据/资源存在;SW 不拦 preview/private;离线不能恢复私有正文/草稿 | 只允许 `public_asset_lru` 记录;业务 DB 无变化 | 不仅凭响应头判断,不缓存 no-store 内容、项目/提示词/session/验证码/未保存编辑 | `cache-enumeration.json`、`service-worker.json`、`trace.zip` | + +#### 6.9.16 WP-6 其余测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP6-ADM-001-role-and-summary` | ADMIN-01、02、04、08、AC-25、49 | active/suspended super_admin、普通/预览账号,后台安全摘要 fixture | 1. 各主体请求后台页/API;2. 停用 admin 后复用旧页面;3. 浏览列表/详情;4. 检查私有默认 | 仅 active super_admin 可进入;停用立即退出;默认只见脱敏元数据,普通/预览无后台入口 | 读取无业务变化;拒绝分支无审计/私有读取;规定登录审计独立存在 | 不共享普通/后台 session,不因预览 grant 获后台,不默认返回提示词/图片/密钥 | 每主体 `response.json`、`db-access.json`、`trace.zip`、`screenshots/admin-denied.png` | +| `TDD-WP6-SVC-001-hard-quota` | ADMIN-05、08、NFR-08、17.15、AC-47 | Resend 日 79/80、月 2399/2400;高德 999/1000;provider 异常;已有 session | 1. 并发领取最后额度;2. 尝试下一次;3. 注入 provider 故障;4. 检查登录/注册/DYN004/现有 session | 最后一份已领取调用可结束,后续不调用;状态 paused_quota/provider;已有 session 继续;手动地点继续 | `external_service_usage` 事务计数/status/reason 更新;失败不创建验证码假成功 | 不发送第 81/超月/第 1001 次,不影响已有 session,不切付费/备用,不建运行时邮件质量状态 | `response.json`、`db-diff.json`、`external-calls.json`、`concurrency-trace.json` | +| `TDD-WP6-SVC-002-limit-recovery` | ADMIN-08、09、NFR-08、AC-47、52 | paused 服务;参数化下调 hard_limit、提高/付费/自动扩容、新周期/供应商恢复 | 1. 尝试各配置;2. 无确认恢复;3. 建立非敏感恢复检查;4. admin 明确确认 | 只允许不扩大能力的下调并审计;提高/付费/自动扩容拒绝;满足条件+确认后恢复 | 成功下调/恢复与 AdminOperationLog 同事务;非法请求零变化 | 不自动恢复、不保存凭据/供应商原文、不把暂停伪装成功或提高上限 | 每参数 `response.json`、`db-diff.json`、`screenshots/service-state.png` | +| `TDD-WP6-AUD-001-sensitive-operations` | ADMIN-09、17.16、AC-50 | 每类规定敏感操作 fixture,含贴纸清理各阶段和 system 配置生效 | 1. 逐类执行成功/拒绝;2. 查询 AdminOperationLog;3. 查询 PrivateContentAccessLog;4. 尝试改删/越权查看 | 每次操作同事务形成不可变、非敏感 before/after/result;两类审计分离;只有 admin 查看 | AdminOperationLog 条数/actor_type/ref/expires_at 正确;私有打开只写专表 | 不漏失败审计、不重复保存私有正文、不记录白名单/密钥/验证码/路径、不允许人工修改删除 | `operation-matrix.json`、`db-diff.json`、`redaction.json`、`trace.zip` | +| `TDD-WP6-STATE-001-three-model-states` | GEN-15、ADMIN-03、08、AC-30、51 | default 不可用、其他推荐、全不可用、恢复参数 | 1. 打开总览/模型页;2. 注入 runtime 变化;3. SSE 后 REST refetch;4. 恢复一个原因 | configured default、逐模型 runtime、recommended 三列独立;推荐可 null;UI 不暗换用户选择 | 只改 runtime/version;ModelConfig/current set 无变化 | 不合并三种状态、不把 SSE 当真相、不回写默认/enabled/priority | `response.json`、`db-diff.json`、`screenshots/model-columns.png`、`trace.zip` | +| `TDD-WP6-DIAG-001-redacted-diagnostics` | PRIV-01、02、ADMIN-08、NFR-09、AC-41、55 | 总览/服务/存储/诊断包含所有状态;注入密钥、邮箱、路径、正文 trap | 1. 请求各摘要;2. 复制诊断;3. 检查托盘诊断;4. 跑脱敏扫描 | 仅显示固定非敏感字段、逻辑位置、计量和状态;复制内容一致 | 只读;可记录非敏感诊断访问/时间,不改变服务状态 | 不含密钥/HMAC/邮箱/提示词/图片/绝对路径/供应商正文,不提供隐藏修复写入口 | `response.json`、`diagnostic.txt`、`redaction.json`、`screenshots/diagnostics.png` | + +#### 6.9.17 WP-7 全部测试族规范 case + +| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `TDD-WP7-REL-001-final-release-record` | NFR-01、09、AC-24、41 | 最终便携 ZIP、START-HERE、SHA-256、RELEASE,真实验收电脑 | 1. 校验制品 hash;2. 导入 Windows build/Chrome/Edge 品牌、major/full version;3. 两浏览器跑全部发布 E2E/视觉/性能;4. 比较候选基线 | RELEASE/制品/诊断/门禁值一致;任一漂移使旧视觉/性能失去发布资格并重跑 | 只生成发布记录/证据;业务 fixture 在隔离验收根 | 不缺 RELEASE、不手填假版本、不接受前一 major/Chromium、不动态换端口 | `RELEASE.json`、`environment.json`、`package-manifest.json`、`evidence.json` | +| `TDD-WP7-EXT-001-three-real-models` | GEN-13、AC-40、41 | 三模型各自真实凭据/配置,显式执行确认 | 1. 显示模型/次数/额度;2. 分别执行纯文字、参考图、四比例、sync/async/查询/响应/错误;3. 脱敏;4. 绑定 config_version | 三模型独立完整矩阵通过才解除;任一缺项保持 externally_blocked | 只写对应模型验证证据/runtime 状态和允许审计;测试任务/点数按隔离规则 | 不共享证据、不自动充值、不保留真实输入/图片/原始响应、不在普通 CI 调用 | 每模型 `contract-matrix.json`、`external-calls.json`、`redaction.json`、`manual-review.json` | +| `TDD-WP7-EXT-002-real-resend` | AUTH-01、02、07、AC-01、33、41、47、49 | 已验证发信域名、正式受控普通/admin 邮箱、免费规则记录 | 1. 核 SPF/DKIM/免费规则;2. QQ/163/至少一种企业邮箱各发 20 封;3. 统计 2 分钟送达;4. 真实完成普通/admin 认证 | 每类至少 19/20 在 2 分钟内且两类认证成功;否则阻止发布但不改 runtime service_status | 只保存人工汇总/证据 hash;不建逐封 EmailVerificationEvent | 不用预置账号/mock 冒充,不保存验证码/真实邮箱进证据,不切付费备用 | `delivery-summary.json`、`domain-check.json`、`auth-result.json`、`redaction.json` | +| `TDD-WP7-EXT-003-real-amap` | DYN-04、PRIV-03、AC-17、41、47 | 个人认证高德 Web 服务 Key、安全绑定和显式确认 | 1. 核接口/QPS/白名单/安全/免费规则;2. 执行定位/逆地理;3. 验证 1,000 硬限模拟/受控测试;4. 脱敏坐标证据 | 接口、绑定和应用硬停止通过才解除;失败保持 externally_blocked | 只写非敏感用量/状态/证据;测试坐标不关联真实项目 | 不暴露 Key/真实用户坐标,不发第 1001 次,不启用付费或自动扩容 | `amap-contract.json`、`external-calls.json`、`redaction.json`、`manual-review.json` | +| `TDD-WP7-UI-001-page-state-evidence` | UIDesign 全页、NFR-02、06、AC-24、25、32、41、55、56 | 第 11.1 节 22 page_id 与适用状态、最终 Chrome/Edge、100%/200% | 1. 逐非 N/A 单元运行 case;2. 截图/键盘/axe;3. 验证全局容量条;4. 对照 Pencil 相关 frame;5. 人工复核 | 所有 UIDesign 页面/状态有实现证据且无重叠;Canvas Index 不实现;无 frame 页面不漏测 | 只生成 UI 证据;交互 DB 变化由所属业务 case 证明 | 不用 PNG OCR 推文案、不把 frame 当页面全集、不以 N/A 逃避容量条/错误态 | 每单元 `trace.zip`、`axe.json`、`screenshots/100pct.png`、`screenshots/200pct.png`、`manual-review.json` | +| `TDD-WP7-SEC-001-artifact-leak-scan` | PRIV-01、02、NFR-09、AC-41、48、56 | ZIP、源码、Git、DB 配置、manifest、日志、截图、trace/evidence 全集合;秘密 trap | 1. 解包并扫描文本/二进制/元数据;2. 检查 Windows 用户名/绝对路径;3. 检查缓存/凭据位置;4. 人工复核命中 | 真实敏感值和硬编码用户路径零命中;凭据仅 Credential Manager;诊断通过同一扫描 | 只生成扫描报告;不修改被扫描制品 | 不把 allowlist/HMAC/验证码/session/提示词/图片/原始错误写证据,不用删除报告掩盖命中 | `scan-report.json`、`allowlist.json`、`manual-review.json` | +| `TDD-WP7-AC-001-trace-structure` | NFR-05、52 当前 AC、109 需求 ID、13 模块、19 契约、9 错误 | 冻结 allowlist、89 family 定义、117 case 定义、页面矩阵和全部 evidence manifest | 1. 运行 `pnpm validate:tdd-trace`;2. 注入缺 AC/重复主责/P0-B passed/未知需求/孤儿 family/case 缺字段/错误父族解析;3. 恢复后重跑 | 每种破坏均非零且定位;正确状态返回 0:52 AC、109 唯一 ID、89/89 family 有 case、117 case 字段齐全 | 只生成 family/trace validation manifest;不改业务 DB | 不信任手写合计、不让 AC-08/26/37/54 计 passed、不允许未知父族、删除末段式解析或空 automation/release_gate | `family-manifest.json`、`trace-validation.json`、`negative-fixtures.json` | + +## 7. 十九个产品数据契约覆盖 + +| 产品契约 | 主测试族 | 核心不变量 | +| --- | --- | --- | +| 17.1 UserProfile | `TDD-WP1-AUTH-*`、`TDD-WP6-PRIV-001` | 角色/状态/名额、点数和告知版本字段 | +| 17.2 ModelConfig | `TDD-WP3-MDL-*` | 唯一默认、唯一正整数优先级、配置与 runtime 分离 | +| 17.3 Project | `TDD-WP2-PROJ-*` | 固定比例、版本、上限、草稿、回收站 | +| 17.4 GenerationJob | `TDD-WP2-GEN-*`、`TDD-WP3-WRK-001` | 单活动任务、快照、不可逆终态、一次结算 | +| 17.5 CanvasElement | `TDD-WP4-CAN-001` | 四类型、最多 50、无 Fabric 私有字段 | +| 17.6 TemplateRegistry | `TDD-WP5-MAN-001`、`TDD-WP5-WHITE-001` | 稳定 ID/版本/状态/白名单与 full_p0 边界 | +| 17.7 StaticStickerCatalog | `TDD-WP5-CAT-001`、`TDD-WP5-CLN-001` | part/顺序/重复、来源、文件关系和引用保护 | +| 17.8 DynamicStickerData | `TDD-WP4-DYN-*` | 只提供消费字段、确认值和 DYN004 坐标 | +| 17.9 CreditAccount/CreditLedger | `TDD-WP2-CRED-*` | available/reserved、不可变流水和唯一操作键 | +| 17.10 PrivateContentAccessLog | `TDD-WP6-PRIV-002`、`TDD-WP6-AUD-001` | 每次打开独立记录、先审计后读取、180 天 | +| 17.11 AnonymousRetainedEvent | `TDD-WP1-DEL-002` | 随机断链主体、允许字段、最迟 180 天 | +| 17.12 AssetReleaseManifest | `TDD-WP5-RES-001` | immutable 版本、哈希、access_class、公开过滤 | +| 17.13 AssetPreviewGrant | `TDD-WP5-PREV-001` | 不改角色、每次校验、撤销/到期立即生效 | +| 17.14 ClientCachePolicy | `TDD-WP0-CACHE-001`、`TDD-WP5-CACHE-001` | 150 MB LRU、公开 allowlist、私有禁存 | +| 17.15 ExternalServiceUsage | `TDD-WP6-SVC-*` | 四状态、硬上限、人工恢复、零自动费用 | +| 17.16 AdminOperationLog | `TDD-WP6-AUD-001` | 两种 actor、非敏感摘要、180 天不可改删 | +| 17.17 GatewayBalanceState | `TDD-WP2-BAL-001`、`TDD-WP3-BAL-001` | model/account/unknown 影响、确认恢复、不回写配置 | +| 17.18 GenerationErrorCategory | `TDD-WP2-ERR-001` | 九项只读注册表、稳定动作与点数口径 | +| 17.19 LocalBackendStorageState | `TDD-WP0-STO-*` | 精确阈值、`>` 准入、file kind 计量、物理后重测 | + +## 8. 十三个功能模块覆盖 + +| FeatureSummary 模块 | 主测试族 | 主责 WP | +| --- | --- | --- | +| 邀请注册与邮箱登录 | `TDD-WP1-AUTH-*`、`TDD-WP7-EXT-002` | WP-1 | +| 点数与原子结算 | `TDD-WP2-CRED-*`、`TDD-WP2-GEN-*` | WP-2 | +| 三模型 AI 生图 | `TDD-WP2-GEN-*`、`TDD-WP3-ADP-*` | WP-2 / WP-3 | +| 项目、历史、保存与冲突 | `TDD-WP2-PROJ-*` | WP-2 | +| 电脑画布与底图编辑 | `TDD-WP4-BG-001`、`TDD-WP4-CAN-001` | WP-4 | +| 文字模板与字体 | `TDD-WP4-TXT-*`、`TDD-WP5-WHITE-001` | WP-4 / WP-5 | +| 25 个普通贴纸 part | `TDD-WP5-CAT-001`、`TDD-WP5-UPL-001`、`TDD-WP5-CLN-001` | WP-5 | +| 色卡与动态贴纸 | `TDD-WP4-COL-001`、`TDD-WP4-DYN-*`、`TDD-WP5-COL-001` | WP-4 / WP-5 | +| 导出 | `TDD-WP2-EXP-*`、`TDD-WP4-EXP-001` | WP-4 | +| 运营后台 | `TDD-WP6-*` | WP-6 | +| 素材发布与内部预览 | `TDD-WP5-MAN-001`、`TDD-WP5-RES-001`、`TDD-WP5-PREV-001` | WP-5 / WP-6 | +| 本机数据与资源访问 | `TDD-WP0-*` | WP-0 / WP-2 | +| 隐私、审计与免费服务边界 | `TDD-WP1-DEL-*`、`TDD-WP6-PRIV-*`、`TDD-WP6-SVC-*` | WP-1 / WP-6 | + +## 9. 九类生成错误测试矩阵 + +### 9.1 GenerationErrorCategory + +`reference_invalid` 与 `safety_rejected` 必须使用不同 fixture、不同模拟响应和不同断言。供应商安全拒绝才能映射为 `safety_rejected`;格式、大小、数量或非安全型内容不可用映射为 `reference_invalid`。 + +| category | 创建前断言 | 创建后断言 | 用户动作 | 点数 | +| --- | --- | --- | --- | --- | +| `upstream_timeout` | 不适用 | failed、无输出、保存 category | 使用原输入重试 | release 一次 | +| `upstream_failed` | 不适用 | failed、无输出、保存 category | 稍后重试 | release 一次 | +| `safety_rejected` | 不适用 | rejected、无输出、使用明确安全证据 | 修改提示词或参考图 | release 一次 | +| `model_disabled` | 不创建任务 | 不适用 | 选择其他模型或等待 | 不 reserve | +| `gateway_balance_insufficient` | 可在创建前阻断 | 已创建则 failed,并更新独立 runtime 影响 | 选择未受影响模型或联系管理员 | 无 reserve 或 release 一次 | +| `gateway_contract_invalid` | contract 非 verified 时阻断 | 已创建后契约失效则 failed | 选择其他模型或联系管理员 | 无 reserve 或 release 一次 | +| `reference_invalid` | 格式/大小/数量可在创建前阻断 | 供应商非安全型不可用时 failed | 更换或移除参考图 | 无 reserve 或 release 一次 | +| `unknown_retryable` | 不适用 | failed,保留非敏感诊断 | 稍后重试 | release 一次 | +| `unknown_non_retryable` | 不适用 | failed,不显示重复重试入口 | 联系管理员 | release 一次 | + +对每类都必须额外断言:前端、GenerationJob、后台生成记录和统计使用同一 category ID;原始错误码、正文、堆栈和内部地址不进入响应或 UI;queued、running、succeeded 的 `error_category` 为空;后台不能新增、重命名或删除分类。 + +### 9.2 稳定工程错误码 + +这些错误不扩充 GenerationErrorCategory。每项都必须验证 HTTP 状态、`error.code`、稳定 `message_key`、correlation ID、允许的 `details`、UI 动作和零敏感泄漏。 + +| code | HTTP | 主测试族 | 关键断言 | +| --- | ---: | --- | --- | +| `BROWSER_UNSUPPORTED` | 426 | `TDD-WP0-BRW-002` | 四个固定 reason;只有阻断引导,无绕过或业务副作用 | +| `MODEL_CONFIG_VERSION_CONFLICT` | 412 | `TDD-WP3-MDL-002` | 返回最新安全版本摘要,整套配置不写入,UI 刷新后重编 | +| `MODEL_DEFAULT_REPLACEMENT_REQUIRED` | 409 | `TDD-WP3-MDL-002` | 停用当前默认却未指定替代时整次失败 | +| `MODEL_DEFAULT_REPLACEMENT_INVALID` | 409 | `TDD-WP3-MDL-002` | 替代不存在/未启用/产生非唯一默认时整次失败 | +| `MODEL_RECOMMENDATION_PRIORITY_INVALID` | 400 | `TDD-WP3-MDL-002` | 缺失、浮点、零或负数定位到字段且不部分写入 | +| `MODEL_RECOMMENDATION_PRIORITY_CONFLICT` | 409 | `TDD-WP3-MDL-002` | 重复优先级标出冲突模型且不部分写入 | +| `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED` | 428 | `TDD-WP6-PRIV-001` | 只返回当前告知版本/message_key,不返回区域数据 | +| `ASSET_HISTORY_REFERENCE_CONFLICT` | 409 | `TDD-WP5-CLN-001` | 任一引用使整批不排队,容量不变并记录 denied 审计 | +| `ASSET_CLEANUP_CANDIDATE_STALE` | 409 | `TDD-WP5-CLN-001` | 旧候选失效,要求重新查询和确认,不复用令牌 | +| `STORAGE_CAPACITY_EXCEEDED` | 507 | `TDD-WP0-STO-002` | full/unavailable 或预计值 `>` 上限才拒绝;等于上限不返回 507 | + +未知 `message_key` 使用 UIDesign 的安全兜底“操作未完成”、correlation ID 和接口允许的重试/返回/诊断动作;不得被映射成成功、已释放点数或允许覆盖。 + +## 10. 视觉、性能与可访问性 + +### 10.1 视觉基线 + +- 页面结构验收使用 `1920 x 1080`、DPR 1、100% 缩放;低保真画板只验证区域、控件、状态、固定文案、焦点和交互,不做像素级 gold image。 +- 200% 浏览器缩放逐页验证无文字覆盖、不可关闭弹层、不可达主动作或不连贯重叠;允许滚动,不缩小字号规避。 +- Canvas 视觉基线固定 OS、浏览器 full version、视口、DPR、字体 ready、动画时刻、背景图和动态数据。 +- Chrome/Edge 元素边界相对基准偏差不超过 2 px。 +- 任一颜色通道绝对差异超过 16/255 的像素视为显著差异,显著像素不超过画布总像素 1%。 +- 任一阈值超限必须人工复核,不能自动判定通过。 +- P0-A 中 COLOR002、COLOR008、COLOR016、DYN012 及其他已知替代/近似项无论自动差异是否通过均人工复核;DYN012 报告必须写明 FONT081 Lexend Deca 替代。 + +### 10.2 性能预算 + +在候选或最终冻结的 Windows、Chrome/Edge、1920 x 1080、DPR 1、50 个混合覆盖元素下: + +| 指标 | 门槛 | +| --- | --- | +| 连续拖动/缩放/旋转 10 秒的指针到下一画面更新 | p95 不超过 50 ms | +| 画布交互帧耗时 | p95 不超过 33 ms | +| 单个主线程长任务 | 不超过 200 ms | +| 连续无响应 | 不得达到 500 ms | +| 自动保存序列化主线程阻塞 | p95 不超过 50 ms | +| 已缓存白名单资源的编辑器重新打开到可操作 | 不超过 3 秒 | +| 1080 x 1920、50 元素导出 | 不超过 10 秒 | +| 导出页面峰值额外内存 | 不超过 1 GiB | +| 普通贴纸面板 DOM | 仅视口和两屏缓冲,不随 1,407 总数线性增长 | + +每项至少运行一次预热和五次测量,报告单次值、p50、p95、最大值、浏览器 full version 和 fixture hash。性能失败不得通过提高超时、减少元素或降低最终导出尺寸掩盖。 + +### 10.3 可访问性 + +- 关键页面运行 axe,critical 和 serious 问题均为 0。 +- 页面第一个 Tab 为“跳到主要内容”;视觉顺序与焦点顺序一致,不使用正 tabindex。 +- 按钮、图标、状态和表单具有可读名称;状态不只依赖颜色。 +- 弹层焦点被限制,关闭后回到触发点;破坏性弹层请求期间不能关闭或重复提交。 +- saving、queued、running、重计量使用 polite live region;保存失败、冲突、浏览器阻断、full/unavailable 使用 assertive。 +- 画布外提供所有关键命令的可聚焦等价控件;焦点或 SSE 更新不得抢走用户当前焦点。 + +## 11. 页面、状态与低保真映射 + +### 11.1 UIDesign 页面 × 适用状态矩阵 + +UIDesign 第“页面与入口地图”及各页面章节是产品页面覆盖全集;Pencil 不定义页面全集。下表的 22 个 coverage unit 包含产品页、身份变体和 Windows 辅助界面。每个非 `N/A` 单元格必须至少关联一个自动或人工 case,并把 `1920 x 1080`、DPR 1、100% 与 200% 缩放截图写入 `artifacts/tdd//ui///`;`N/A` 必须保留下表理由,不能用缺实现代替。内部预览账号沿用普通页面,只作为授权状态变体,不创建新角色或后台页面。 + +| page_id / 页面或入口 | 主体与入口 | 正常 | 空 / 加载 | 失败 / 禁用 | 冲突 / 容量 / 特殊状态 | 主测试落点 | +| --- | --- | --- | --- | --- | --- | --- | +| `support-gate` 浏览器不受支持引导 | 所有主体,产品门禁前 | 四种阻断 reason、支持品牌/major、无继续入口 | N/A:引导无业务空态;身份不可判定属于阻断 | bundle/API 均无法绕过;诊断仅最小字段 | N/A:不读取业务状态或容量 | `TDD-WP0-BRW-002`、`TDD-WP7-UI-001` | +| `user-auth` 登录 / 注册 | 未登录普通入口 | 登录、分阶段注册、管理员文字入口、固定本机提示 | 字段初始空;发码/验证/注册加载 | Resend、验证码、邀请、名额、告知未完成;按钮禁用 | N/A:认证不消费存储容量 | `TDD-WP1-AUTH-*`、第 6.9.2 节 | +| `workspace` 创作工作台 | active 普通/内部预览账号 | 生成表单、模型/点数/比例、最近项目 | 无项目艺术带;模型/项目加载 | 服务/参考图/生成错误;模型、点数、单任务、历史/项目上限禁用 | model config 变化重确认;warning/critical 提示;full/unavailable 阻断新增 | `TDD-WP2-GEN-*`、`TDD-WP0-STO-*` | +| `current-task` 当前生成任务 | active 普通/内部预览账号 | 真实状态、结果入口 | 无活动任务;queued/running 无假百分比 | failed/rejected 与九类固定动作;第二任务禁用 | storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条 | `TDD-WP2-GEN-*`、`TDD-WP3-WRK-001` | +| `projects` 项目列表与回收站 | active 普通/内部预览账号 | active/trashed 列表、筛选、多选 failed_empty | 无项目/筛选空;稳定卡片骨架 | 列表失败保留旧数据;20 active、恢复/批量条件禁用 | 多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条 | `TDD-WP2-PROJ-001`、`TDD-WP2-PROJ-003`、`TDD-WP2-PROJ-005` | +| `project-detail` 项目详情 | 项目所有者 | 历史、固定比例、继续生成、编辑、原图/latest 下载 | 无成功图草稿;历史/成品加载 | 10 图上限、当前底图不可删、无 latest、资源读取失败 | 比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条 | `TDD-WP2-PROJ-*`、`TDD-WP2-EXP-002` | +| `editor` 编辑器 | 有成功底图的项目所有者 | 三栏、保存状态、画布、属性与素材 | 画布/素材加载;无成功图不能正常进入 | 保存/素材失败;第 51 元素、不可用模板禁用 | dirty/saving/saved/failed;conflicted 只读;full 可存 JSON,unavailable 保存失败 | `TDD-WP4-*`、第 6.9.8 节 | +| `export` 导出弹层 | 编辑器内项目所有者 | JPG/PNG、质量、尺寸、下载/latest 分结果 | 合成/持久化加载;无业务空态 | 合成、下载、项目保存、latest 保存分别失败;无画布/冲突次数用尽禁用 | 未提交确认;conflicted 仅一次本地导出;full/unavailable 后端失败不撤回下载 | `TDD-WP4-EXP-001`、第 6.9.9 节 | +| `credits` 点数明细 | active 普通/内部预览账号 | available/reserved、流水、关联与时间 | 无流水;分页加载 | 加载失败保留安全余额摘要 | 负 available 如实显示;无购买/充值入口;页面操作不消费容量但仍继承普通端全局容量条 | `TDD-WP2-CRED-*`、第 6.9.3 节 | +| `settings` 设置与账号注销 | active 普通/内部预览账号 | 资料、本机数据、容量摘要、固定文案、注销 | 资料/状态加载 | 保存失败;注销确认/处理中禁用重复提交 | full/unavailable 状态与允许动作;注销后返回登录 | `TDD-WP1-DEL-*`、`TDD-WP0-DATA-002` | +| `preview-user-variant` 内部预览账号普通页变体 | 有有效/撤销/过期 AssetPreviewGrant 的普通账号 | 授权批次素材显示“预览”,其余页面同普通用户 | 无授权/批次空;manifest 加载 | 撤销/过期立即阻断新读取 | 不出现后台入口、调点、邀请码、模型或私有内容;继承普通端全局容量条 | `TDD-WP5-PREV-001`、`TDD-WP5-RES-001` | +| `admin-auth` super_admin 登录 | 未登录后台入口 | 白名单邮箱验证码、独立 audience、返回普通登录 | 字段空;发码/登录中 | 非白名单发码前阻断、停用/移除白名单、Resend 错误 | N/A:不显示普通创作或容量业务 | `TDD-WP1-ADM-001`、`TDD-WP1-CFG-001` | +| `admin-overview` 后台总览 | active super_admin | 名额、任务、模型、点数、服务、容量、审计摘要 | 无近期事件;指标骨架 | 聚合失败保留旧值和时间;敏感动作按状态禁用 | default/runtime/recommended 分列;pending_manual_review 为成本核对;容量持续条 | `TDD-WP6-ADM-001`、`TDD-WP6-STATE-001`、`TDD-WP6-DIAG-001` | +| `admin-users` 用户管理 | active super_admin | 脱敏列表/详情、暂停恢复、调点 | 无用户/筛选空;表格骨架 | 操作失败、原因缺失、状态前置禁用 | 调点可使 available 为负但 reserved 不变;页面操作不消费容量但仍继承后台全局容量条 | `TDD-WP6-ADM-001`、`TDD-WP2-CRED-002` | +| `admin-invites` 邀请码 | active super_admin | 创建、停用、期限/次数、脱敏复制 | 无邀请码;表格骨架 | 名额满、停用/过期/耗尽不可当可用;请求失败 | CAS/幂等结果;页面操作不消费容量但仍继承后台全局容量条 | `TDD-WP1-AUTH-*`、`TDD-WP1-SLOT-001`、`TDD-WP6-AUD-001` | +| `admin-models` 模型 | active super_admin | 完整三模型集合、default/runtime/recommended 三层 | 初始加载骨架;无 recommended 是合法状态 | 合法替代/priority 前置禁用;契约未验证/运行时不可用 | config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条 | `TDD-WP3-MDL-*`、第 6.9.7 节 | +| `admin-assets` 素材 | active super_admin | 普通贴纸、资源版本/发布、历史清理 | 无上传素材/无候选;列表/处理加载 | 格式/编译失败;权限、文件状态禁用 | stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量 | `TDD-WP5-UPL-001`、`TDD-WP5-CLN-001`、`TDD-WP5-MAN-001` | +| `admin-preview` 内部预览 | active super_admin | 批次、模板状态、账号授权/撤销/到期 | 无批次/授权;表格骨架 | 非法状态转换、过期、发布校验失败 | 授权不改变角色;P0-B 全量开放不可在 P0-A 出现;继承后台全局容量条 | `TDD-WP5-PREV-001`、`TDD-WP5-WHITE-001` | +| `admin-generations` 生成记录与私有内容 | active super_admin | 默认安全摘要、告知、逐次打开 | 无记录;列表/审计写入加载 | 未确认 428、审计失败不打开、权限禁用 | 告知升版重阻断;每次打开先独立审计;页面读取不消费容量但仍继承后台全局容量条 | `TDD-WP6-PRIV-*`、第 6.9.6 节 | +| `admin-services-storage` 服务与存储 | active super_admin | Resend/高德/网关/Worker/LocalDataRoot 状态与允许动作 | 指标加载;无告警 | paused/degraded/unavailable;不合法恢复/提高上限禁用 | full/unavailable、清理排队和物理重计量;不显示绝对路径 | `TDD-WP6-SVC-001`、`TDD-WP6-SVC-002`、`TDD-WP6-DIAG-001`、`TDD-WP0-STO-*` | +| `admin-audit` 审计 | active super_admin | 后台操作与私有访问两类独立页/标签 | 无记录;游标加载 | 查询失败保留旧结果;无修改/人工删除动作 | 180 天保留;正文、图片、密钥和路径不出现;页面读取不消费容量但仍继承后台全局容量条 | `TDD-WP6-AUD-001`、`TDD-WP6-PRIV-002` | +| `system-ui` Windows 辅助系统界面 | 本机托盘/Supervisor,无产品角色 | 托盘、运行状态、打开 Dada、诊断、退出 | 启动中;N/A 业务空态 | 启动失败、端口占用、API/Worker degraded,动作严格受限 | full/unavailable 只作脱敏告警;固定端口不动态切换 | `TDD-WP0-SUP-001`、`TDD-WP0-PKG-001`、`TDD-WP0-LOG-001`、`TDD-WP7-REL-001` | + +`pnpm validate:tdd-trace` 必须把 UIDesign 页面/入口 allowlist 与上述 `page_id` 对照:缺少 UIDesign 页面、重复 page_id、把内部预览误建角色/后台、把 Canvas Index 计为产品页、适用状态无 case/evidence 或 `N/A` 无理由时返回非零退出码。 + +### 11.2 Pencil 布局参考映射 + +`.pen` 中共有 19 个顶层画板。画板尺寸 1600 x 900,索引画板为 1440 x 420;预览 PNG 外边框使图像为 1602 x 902 或 1442 x 422。实现验收仍以 UIDesign 的 1920 x 1080 为主,不把预览尺寸改成产品视口。 + +画板中的版本号、账号、点数、容量和时间均是低保真示例数据。例如辅助界面预览里的 Chrome/Edge 版本不能作为支持记录;自动化必须从当前测试 fixture 读取状态,发布验收必须从 `RELEASE.json` 读取真实版本。若 `.pen` 或预览 PNG 中的示例文案、账号、容量、时间与 UIDesign 固定文案冲突,以 UIDesign 为界面验收依据;自动化不得通过 PNG OCR 反推验收文案。 + +| frame ID | 画板 | 预览 | 主要测试落点 | +| --- | --- | --- | --- | +| `Z8p3I` | 00 Canvas Index | `Z8p3I.png` | 非产品索引,仅校验设计资产清单;不要求实现,不计页面完成率 | +| `Z0pf8` | 01A 普通用户登录 | `Z0pf8.png` | 登录字段、标签、管理员入口、固定本机提示、焦点 | +| `DVPM8` | 01B 注册展开态 | `DVPM8.png` | 邀请/邮箱只读摘要、资料、告知、注册主动作 | +| `WbDYT` | 02A 空工作台 | `WbDYT.png` | 艺术带、生成表单、推荐模型、比例和空任务 | +| `d99Lo` | 02B 生成中工作台 | `d99Lo.png` | 当前任务、冻结点数、critical 容量条和禁用提交 | +| `zXyG9` | 03A 项目列表 | 无独立 PNG | 项目/回收站、上限、failed_empty、稳定卡片 | +| `v2Qbu` | 03B 项目详情 | 无独立 PNG | 历史、固定比例、编辑/生成/下载、无成品状态 | +| `bHQnJ` | 04A 三栏编辑器 | `bHQnJ.png` | 三栏、保存状态、画布、对象属性、固定底栏 | +| `i9upg7` | 04B 导出弹层 | 无独立 PNG | JPG/PNG、质量、未提交确认、下载/持久化分结果 | +| `YlKzE` | 05A 点数明细 | 无独立 PNG | available/reserved、流水、无购买入口 | +| `Ws5E0` | 05B 设置与本机数据 | 无独立 PNG | 固定文案、容量、文件权限风险、注销 | +| `e4e2Kz` | 06A 后台总览 | `e4e2Kz.png` | 名额、任务、成本核对、服务/容量、最近审计 | +| `YVfVG` | 06B 模型配置 | 无独立 PNG | 默认/推荐/runtime 三列、完整集合和冲突 | +| `A28V3c` | 06C 素材与历史清理 | 无独立 PNG | 上传计量、候选/确认/冲突/排队/重计量 | +| `OnsiT` | 06D 生成记录与私有告知 | 无独立 PNG | 默认隐藏正文、身份+版本告知、逐次入口 | +| `L3lde2` | 06E 服务与存储 | 无独立 PNG | Resend/高德/网关/Worker/storage 状态与动作 | +| `DlCpN` | 06F 私有内容访问审计 | 无独立 PNG | 两类审计分离、不可编辑删除、无正文 | +| `u1adni` | 07A 浏览器硬阻断 | 无独立 PNG | 426、四原因、支持 major、无继续入口 | +| `awt96` | 07B Windows 辅助系统界面 | `awt96.png` | 托盘、启动失败、端口占用、degraded、诊断脱敏 | + +除 `Z8p3I` Canvas Index 外,Pencil 产品 frame 只为其映射到的第 11.1 节页面/状态提供布局参考;是否需要截图及状态数量由第 11.1 节决定,不以“18 个产品 frame”替代完整页面覆盖。UIDesign 中没有独立 Pencil frame 的后台用户、邀请码、内部预览、管理员登录等页面仍必须具备完整 UI 证据。预览缺失不阻止测试,`.pen` 不是实现范围清单;不得自行补画或改写设计文件作为测试通过手段。 + +## 12. 当前 AC 唯一主责矩阵 + +每项 AC 只有一个主责 WP。支持测试可以跨 WP,但只能由主责 WP 汇总完成,WP-7 只汇总发布证据而不夺取产品需求主责。 + +`pnpm validate:tdd-trace` 必须从冻结的 PRD 需求/AC allowlist 校验本节、第 13 节和第 6.9 节,失败时返回非零退出码:当前 AC 集合必须恰为 52 项;每项恰有一个唯一主责;AC-08、AC-26、AC-37、AC-54 不得进入当前矩阵或 `passed` 计数;第 13 节范围展开后必须恰为 109 个互不重复的已知 PRD 需求 ID,不得遗漏、重复或新增未知 ID;测试族必须恰为 89 个且 AREA 正则允许数字;117 个规范 case 必须全部按 `^(TDD-WP[0-7]-[A-Z0-9]+-[0-9]{3})-` 捕获已知父族,每个父族至少一个 case;每个 case 的 fixture、步骤、响应、DB/文件变化、禁止副作用、证据文件和第 2.2.1 节派生属性均非空。检查必须读取结构化追踪数据或确定性解析本文,不得信任手写合计值。 + +| AC | 当前范围 | 唯一主责 | 主测试族 | 必要支持 | +| --- | --- | --- | --- | --- | +| AC-01 | 全部 | WP-1 | `TDD-WP1-AUTH-001` | `TDD-WP1-NOTICE-001`、`TDD-WP7-EXT-002` | +| AC-02 | 全部 | WP-1 | `TDD-WP1-AUTH-002` | 无 | +| AC-03 | 全部 | WP-2 | `TDD-WP2-GEN-002` | `TDD-WP3-ADP-001` | +| AC-04 | 全部 | WP-2 | `TDD-WP2-GEN-002` | `TDD-WP2-ERR-001` | +| AC-05 | 全部 | WP-2 | `TDD-WP2-GEN-001` | `TDD-WP2-CRED-001` | +| AC-06 | 全部 | WP-2 | `TDD-WP2-PROJ-001` | 无 | +| AC-07 | 全部 | WP-4 | `TDD-WP4-BG-001` | `TDD-WP4-BG-002`、`TDD-WP4-COL-001` | +| AC-09 | 全部 | WP-4 | `TDD-WP4-TXT-001` | `TDD-WP4-EXP-001` | +| AC-10 | 全部 | WP-4 | `TDD-WP4-TXT-001` | 无 | +| AC-11 | P0-A 字体子范围 | WP-4 | `TDD-WP4-TXT-002` | `TDD-WP5-WHITE-001` | +| AC-12 | 全部 | WP-4 | `TDD-WP4-TXT-002` | `TDD-WP4-EXP-001` | +| AC-13 | 全部 | WP-5 | `TDD-WP5-CAT-001` | `TDD-WP5-CACHE-001` | +| AC-14 | 全部 | WP-4 | `TDD-WP4-STK-001` | 无 | +| AC-15 | 仅四种 P0-A 色卡 | WP-5 | `TDD-WP5-COL-001` | `TDD-WP4-COL-001` | +| AC-16 | 全部 | WP-4 | `TDD-WP4-DYN-001` | 无 | +| AC-17 | 全部 | WP-4 | `TDD-WP4-DYN-002` | `TDD-WP7-EXT-003` | +| AC-18 | 全部 | WP-4 | `TDD-WP4-DYN-003` | 无 | +| AC-19 | 全部 | WP-4 | `TDD-WP4-DYN-003` | `TDD-WP4-VIS-001` | +| AC-20 | 全部 | WP-2 | `TDD-WP2-PROJ-002` | `TDD-WP0-CACHE-001` | +| AC-21 | 全部 | WP-2 | `TDD-WP2-PROJ-003` | `TDD-WP0-STO-003` | +| AC-22 | 全部 | WP-1 | `TDD-WP1-DEL-001` | `TDD-WP1-DEL-002` | +| AC-23 | 仅 P0-A 四比例与同机下载 | WP-4 | `TDD-WP4-EXP-001` | `TDD-WP2-EXP-001` | +| AC-24 | 全部 | WP-0 | `TDD-WP0-BRW-001`、`TDD-WP0-BRW-002` | `TDD-WP0-BND-001` | +| AC-25 | 全部 | WP-6 | `TDD-WP6-PRIV-001`、`TDD-WP6-PRIV-002` | `TDD-WP6-AUD-001` | +| AC-27 | 全部 | WP-4 | `TDD-WP4-CAN-001` | `TDD-WP4-PERF-001` | +| AC-28 | 全部 | WP-2 | `TDD-WP2-CRED-001` | `TDD-WP2-GEN-003` | +| AC-29 | 全部 | WP-2 | `TDD-WP2-CRED-002` | `TDD-WP2-GEN-001` | +| AC-30 | 全部 | WP-3 | `TDD-WP3-MDL-002`、`TDD-WP3-MDL-003` | `TDD-WP2-GEN-003` | +| AC-31 | 全部 | WP-5 | `TDD-WP5-UPL-001`、`TDD-WP5-CLN-001` | `TDD-WP0-STO-003` | +| AC-32 | 全部 P0-A 白名单 | WP-4 | `TDD-WP4-VIS-001` | `TDD-WP4-TXT-003`、`TDD-WP5-WHITE-001`、`TDD-WP7-REL-001` | +| AC-33 | 全部 | WP-1 | `TDD-WP1-AUTH-003` | `TDD-WP7-EXT-002` | +| AC-34 | 全部 | WP-2 | `TDD-WP2-PROJ-001` | 无 | +| AC-35 | 全部 | WP-2 | `TDD-WP2-EXP-001` | `TDD-WP2-PROJ-002` | +| AC-36 | 仅同机多标签 | WP-2 | `TDD-WP2-PROJ-004` | `TDD-WP2-EXP-001` | +| AC-38 | 仅 P0-A 本机成品空状态 | WP-2 | `TDD-WP2-EXP-002` | 无 | +| AC-39 | 全部 | WP-1 | `TDD-WP1-DEL-002` | `TDD-WP6-AUD-001` | +| AC-40 | 全部 | WP-3 | `TDD-WP3-ADP-001`、`TDD-WP3-ADP-002` | `TDD-WP7-EXT-001` | +| AC-41 | 全部 | WP-7 | `TDD-WP7-REL-001`、`TDD-WP7-EXT-*` | WP-0 至 WP-6 全部 Green | +| AC-42 | 全部 | WP-5 | `TDD-WP5-PREV-001` | `TDD-WP5-RES-001` | +| AC-43 | 全部 | WP-2 | `TDD-WP2-PROJ-005` | `TDD-WP2-GEN-002` | +| AC-44 | 全部 | WP-2 | `TDD-WP2-REF-001` | `TDD-WP0-CACHE-001` | +| AC-45 | 全部 | WP-1 | `TDD-WP1-SLOT-001` | `TDD-WP5-PREV-001` | +| AC-46 | 全部 | WP-0 | `TDD-WP0-CACHE-001` | `TDD-WP5-CACHE-001` | +| AC-47 | 全部 | WP-6 | `TDD-WP6-SVC-001` | `TDD-WP7-EXT-002`、`TDD-WP7-EXT-003` | +| AC-48 | 全部 | WP-5 | `TDD-WP5-RES-001` | `TDD-WP5-PREV-001`、`TDD-WP0-SEC-001` | +| AC-49 | 全部 | WP-1 | `TDD-WP1-ADM-001`、`TDD-WP1-CFG-001` | `TDD-WP7-EXT-002` | +| AC-50 | 全部 | WP-6 | `TDD-WP6-AUD-001` | `TDD-WP5-CLN-001`、`TDD-WP6-PRIV-002` | +| AC-51 | 全部 | WP-2 | `TDD-WP2-BAL-001` | `TDD-WP3-BAL-001` | +| AC-52 | 全部 | WP-6 | `TDD-WP6-SVC-002` | `TDD-WP6-AUD-001` | +| AC-53 | 全部 | WP-2 | `TDD-WP2-ERR-001` | `TDD-WP3-ADP-001` | +| AC-55 | 全部 | WP-0 | `TDD-WP0-STO-001`、`TDD-WP0-STO-002`、`TDD-WP0-STO-003` | `TDD-WP5-UPL-001`、`TDD-WP5-CLN-001` | +| AC-56 | 全部 | WP-0 | `TDD-WP0-DATA-002` | `TDD-WP7-UI-001` | + +未来 AC-08、AC-26、AC-37、AC-54 不得出现在当前 `passed` 计数。允许编译器或内部预览为未来素材生成非发布证据,但不得扩大普通用户可见范围或阻止 P0-A 完成。 + +## 13. 109 个 PRD 需求 ID 覆盖索引 + +| 主责 WP | 需求 ID | 数量 | +| --- | --- | ---: | +| WP-0 | PRIV-01、PRIV-02、NFR-01、NFR-07、NFR-09 | 5 | +| WP-1 | AUTH-01 至 AUTH-07、PRIV-03、PRIV-04、PRIV-06、NFR-04 | 11 | +| WP-2 | CREDIT-01 至 CREDIT-06、PROJECT-01 至 PROJECT-09、GEN-03 至 GEN-06、GEN-11、GEN-12、GEN-14、GEN-16、NFR-05 | 24 | +| WP-3 | GEN-01、GEN-02、GEN-07 至 GEN-10、GEN-13、GEN-15、ADMIN-03 | 9 | +| WP-4 | EDITOR-01 至 EDITOR-09、TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、EXPORT-01 至 EXPORT-06、NFR-03、NFR-06 | 45 | +| WP-5 | STATIC-01 至 STATIC-04、ADMIN-06、ADMIN-07、PRIV-05、NFR-02 | 8 | +| WP-6 | ADMIN-01、ADMIN-02、ADMIN-04、ADMIN-05、ADMIN-08、ADMIN-09、NFR-08 | 7 | +| 合计 | 不重复的当前 PRD 需求 ID | 109 | + +每个具体测试文件必须从本表选择需求 ID,不得创造未经 PRD 定义的新 ID。跨包依赖可以重复引用需求 ID,但唯一主责不变。 + +## 14. CI、隔离与外部验证 + +### 14.1 普通 CI + +- Gitea Actions 使用冻结 lockfile,全部外部服务为 mock,仅使用脱敏 fixture 和 `.invalid` 邮箱。 +- 每个 DB/文件测试使用系统临时目录和独立 SQLite;完成后关闭句柄并删除测试目录。 +- 浏览器与打包 job 并发数为 1,使用 `%RUNNER_TEMP%` 下的独立 user-data-dir,不读取或关闭真实 Dada 实例。 +- 普通 CI 使用系统分配临时回环端口;临时端口仅限测试 harness。打包、Supervisor、启动入口、诊断和发布验收必须证明产品包固定使用 `127.0.0.1:43121` 且端口占用时失败,不会动态换端口。 +- 普通 CI 不读取 Windows Credential Manager 中真实条目,不访问项目方真实 LocalDataRoot,不扫描 3.57 GB 归档。 +- flaky 重跑不能把失败改成通过。任何自动重跑都保留首轮失败,并在证据中记录;连续不稳定测试阻止所属 WP 完成。 + +### 14.2 受控真实验证 + +- `pnpm validate:external` 只能由项目方在 P0-A 验收电脑显式运行;命令从 Windows Credential Manager 按最小范围读取凭据,不接收命令行密钥。 +- 每次真实调用先显示服务、模型、预计调用数量和是否可能消耗既有额度;执行者明确确认后才开始。 +- 真实响应立即脱敏,仅保留协议字段、状态类别、耗时、MIME、尺寸、非敏感用量、证据哈希和时间。 +- AI 网关三个模型分别生成证据,不能共享“已验证”标记。 +- Resend 送达记录只保存人工统计,不建立运行时 EmailVerificationEvent;19/20、2 分钟的阻断门槛直接来自 PRD 第 23.2 节、AC-41、AC-47 和 DevelopmentPlan 第 1.4、11.1 节,不是 TDD 新增指标。 +- 高德真实测试不把测试坐标关联到真实用户项目。 +- 外部验证失败保持 externally_blocked,不自动切换付费、备用供应商、自动充值或自动扩容。 + +## 15. 工作包顺序与完成门禁 + +| WP | 依赖 | 自动完成门禁 | 仍需真实/人工门禁 | +| --- | --- | --- | --- | +| WP-0 | 无 | 单元、API、存储、Supervisor、安全、打包 Green | RELEASE 浏览器记录在 WP-7 冻结 | +| WP-1 | WP-0 | 认证状态机、事务、会话、名额、注销、审计 Green | Resend 正式认证 | +| WP-2 | WP-0、WP-1 | 项目、点数、任务、参考图、冲突、错误 Green | 真实三模型完成态依赖 WP-3 | +| WP-3 | WP-2 | 模型集合、runtime、Adapter mock、Worker 恢复 Green | 三模型独立真实契约 | +| WP-4 | WP-2 | 编辑器、导出、a11y、性能 harness Green | 最终视觉依赖 WP-5 真实白名单 | +| WP-5 | WP-0;与 WP-4 前半可并行 | 编译、目录、隔离、缓存、上传、清理 Green | 真实归档只读校验和人工视觉 | +| WP-6 | WP-1、WP-2、WP-5;模型页依赖 WP-3 | 后台、告知、审计、服务与存储 Green | 真实服务恢复和正式身份流程 | +| WP-7 | WP-0 至 WP-6 | `pnpm test:all`、52 AC 结构检查、制品扫描 Green | RELEASE、Chrome/Edge、AI、Resend、高德及人工验收全部通过 | + +任何工作包的“不能提前宣告”条件沿用 DevelopmentPlan。依赖工作包未 Green 时,下游可以编写 Red 测试和 mock 接线,但不能标记完成。 + +## 16. P0-A 测试完成定义 + +只有以下条件全部满足,`tdd.md` 所定义的 P0-A 测试才完成: + +1. 四份 Markdown 全局来源哈希与第 1.2.1 节一致;第 1.2.2 节 UI 参考资产已匹配当前哈希,或受影响页面已完成局部重读、映射更新和视觉重建,不存在未处理的 `source_changed` case。 +2. WP-0 至 WP-7 全部达到第 15 节门禁,第 6.9 节 117 个规范 case 及其参数分支均有可复现 Red 和最终 Green/Passed 证据。 +3. `pnpm validate:tdd-trace` 通过;第 12 节 52 项当前 AC 全部 passed,每项恰有一个唯一主责;AC-08、26、37、54 未混入当前完成统计;89/89 测试族至少有一个 case,117 个 case 的必填字段、派生执行属性和证据目录完整。 +4. 第 13 节 109 个互不重复且无未知项的需求 ID、十三个功能模块、十九个数据契约和九类错误均有可执行测试落点。 +5. `reference_invalid` 与 `safety_rejected` 由不同证据证明;九类错误的任务、点数、用户动作和脱敏断言全部通过。 +6. `managed_content_bytes` 的阈值、等号写入、超限无副作用、原图/缩略图计量、full/unavailable 动作和物理后重测全部通过。 +7. ModelConfig 始终一个 enabled default、唯一正整数 priority;runtime 降级不回写配置;recommended 可重新派生或为空。 +8. 私有告知按 super_admin 身份+版本跨标签、重启、重登和两浏览器持久;每次图片/完整提示词读取仍先写独立访问审计。 +9. Playwright 已实际枚举 Service Worker、Cache Storage 和 IndexedDB,确认没有 internal preview、私有内容、项目、提示词或未保存草稿。 +10. 跨用户隔离使用正常邀请/验证码/普通权限创建临时第二账号,完成后正常注销并进入规定清理路径。 +11. 第 11.1 节 UIDesign 页面 × 状态矩阵的全部适用单元格均有实现和证据;Canvas Index 未计入产品页,UIDesign 中没有独立 Pencil frame 的页面没有漏测;1920 x 1080、200% 缩放、焦点、最长文案、弹层和多状态叠加无不可用重叠。 +12. 视觉差异、50 元素性能、四比例导出、Chrome/Edge 下载与文件选择均在最终 RELEASE 版本通过。 +13. 三模型、Resend、高德真实门槛通过;未配置自动充值、收费备用或自动扩容。 +14. `RELEASE.json`、便携 ZIP、SHA-256、START-HERE 和固定端口边界一致;局域网、其他设备和不支持浏览器无法进入产品。 +15. 制品、代码、日志、DB 配置、manifest、截图和证据中不存在真实凭据、验证码、会话令牌、管理员真实邮箱、完整私有内容或硬编码用户绝对路径。 +16. 普通用户主框架和设置区持续原样显示:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。” + +缺少任一项时保持各 case 的实际测试状态,不得标记 P0-A 测试整体完成或发布完成。