Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
660 kB · 16830 lines
Zig
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831const std = @import("std");const display_width = @import("../../core/shared/display_width.zig");const io_mod = @import("../../core/shared/io.zig");const types = @import("../../core/shared/types.zig");const command_output_content = @import("../../core/tooling/command_output_content.zig");const full_transcript_screen = @import("../full_transcript_screen.zig");const input_visual_layout = @import("../input/visual_layout.zig");const render_engine = @import("../render_engine.zig");const command_output_runtime = @import("command_output_runtime.zig");const transcript_painter = @import("painter.zig");const transcript_runtime = @import("runtime.zig");const transcript_store = @import("store.zig");const transcript_writer = @import("writer.zig");const ui_render = @import("../render.zig");const user_message_card = @import("../assistant/user_message_card.zig");const vt_emulator = @import("../../core/terminal/engine.zig");
const Allocator = std.mem.Allocator;const Layout = types.Layout;const Metrics = types.Metrics;const transcript_blocks = render_engine.transcript_blocks;const viewport_selection = render_engine.viewport_selection;
const RawEntryClass = transcript_blocks.RawEntryClass;const Styles = transcript_blocks.Styles;const ToolDetailRecord = transcript_blocks.ToolDetailRecord;const TranscriptPreparationSource = transcript_runtime.TranscriptPreparationSource;const TranscriptRuntime = transcript_runtime.TranscriptRuntime;const HardLineStarts = viewport_selection.HardLineStarts;const TranscriptBuffer = viewport_selection.TranscriptBuffer;const VisibleTranscriptLine = viewport_selection.VisibleTranscriptLine;fn renderEntriesToBytes(alloc: Allocator, entries: []const transcript_blocks.TranscriptEntry, cols: u16) ![]u8 { return transcript_runtime.renderEntriesToBytes(alloc, entries, cols, .{});}const visualRowsForLine = transcript_blocks.visualRowsForLine;
fn resolveAndSealTranscriptTransitionForTest( runtime: *TranscriptRuntime, alloc: Allocator, source: *TranscriptPreparationSource, prepared: *transcript_painter.PreparedTranscriptSurfacePaint, target_layout: render_engine.frame_layout.CommittedLayoutSnapshot, plan: *render_engine.paint_plan.PaintPlan, scroll_plan: render_engine.frame_scroll_plan.FrameScrollPlan,) !transcript_runtime.TranscriptTransition { const scroll_facts = try runtime.prepareTranscriptScrollFactsForFrame( alloc, source, prepared, false, false, ); const destructive_invalidation = render_engine.frame_retention.transcriptAreaHasDestructiveInvalidation( runtime.committed_frame_layout.transcript_area, plan.invalidation, ); const resolved = try runtime.resolveTranscriptTransitionTargetForFrame( alloc, source, prepared, target_layout, scroll_plan, scroll_facts, destructive_invalidation, plan.activity == .overlay_entry, ); resolved.applyToPaintPlan(plan); return runtime.sealTranscriptTransition( alloc, source, prepared, plan, resolved, );}
fn resolveVisibleLineForTest(line: VisibleTranscriptLine, buf: TranscriptBuffer) []const u8 { return switch (line) { .transcript => |t| t.ref.resolve(buf), .folded_line => |f| f.text, };}
fn commandOutputTestRuntime(sink: std.Io.File) TranscriptRuntime { return .{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, }, };}
fn testSelection(start_line: usize) viewport_selection.ViewportSelection { return .{ .top_row = 1, .bottom_row = 4, .start_line = start_line, .partial_skip_rows = 0, .line_count = 16, };}
fn transcriptTestLayout(cols: u16, rows: u16, content_bottom: u16) Layout { return .{ .rows = rows, .cols = cols, .content_bottom = content_bottom, .divider_top_row = content_bottom + 1, .input_row = content_bottom + 2, .divider_bottom_row = content_bottom + 3, .hint_row = rows, };}
fn makeLongPastePrompt(alloc: Allocator, line_count: usize) ![]u8 { var out: std.Io.Writer.Allocating = .init(alloc); defer out.deinit();
try out.writer.writeAll("LONG_PASTE_FIRST_MARKER\n"); var index: usize = 0; while (index < line_count) : (index += 1) { try out.writer.print("long-paste-line-{d}\n", .{index}); } try out.writer.writeAll("LONG_PASTE_LAST_MARKER\n"); return out.toOwnedSlice();}
fn installStableAnchorWithOriginForTest( runtime: *TranscriptRuntime, alloc: Allocator, flow: []const u8, selection: viewport_selection.ViewportSelection, visual_offset: u32, cursor_row: u16, cursor_col: u16, layout_id: u64, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin,) !void { var target_cache: std.ArrayList(u8) = .empty; errdefer target_cache.deinit(alloc); try target_cache.appendSlice(alloc, flow); var transition = transcript_runtime.TranscriptTransition{ .scroll_plan = render_engine.frame_scroll_plan.FrameScrollPlan.none( runtime.layout.rows, runtime.owned_top_row, ), .document_append = .{}, .materialized_without_append = .{}, .target_flow = try alloc.dupe(u8, flow), .target_cache = target_cache, .target_cache_origin_untrimmed = runtime.transcript_cache_origin_untrimmed, .folded_summary_indices = &.{}, .target_layout = .{ .layout_id = layout_id, .owned_top = runtime.owned_top_row, .owned_band = .{ .top = runtime.owned_top_row, .bottom = runtime.layout.rows }, .body_area = .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, .transcript_area = .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, .footer_area = .{ .top = runtime.layout.content_bottom + 1, .bottom = runtime.layout.rows }, .solved_frame_height = runtime.layout.rows - runtime.owned_top_row + 1, .terminal_rows = runtime.layout.rows, .terminal_cols = runtime.layout.cols, }, .selection = selection, .visual_offset = visual_offset, .history_visual_offset = visual_offset, .total_visual_rows = visual_offset + 64, .cursor_row = cursor_row, .cursor_col = cursor_col, .replaceable_last_line = false, .replaceable_start = 0, .replaceable_row = cursor_row, .trace_transcript_frame = false, .trace_visible_rows = selection.bottom_row - selection.top_row + 1, .measured_history_origin = measured_history_origin, }; defer transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 0, .changed_cells = 0, .full_repaint = false, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = cursor_row, .committed_cursor_col = cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });}
fn installStableAnchorForTest( runtime: *TranscriptRuntime, alloc: Allocator, flow: []const u8, selection: viewport_selection.ViewportSelection, visual_offset: u32, cursor_row: u16, cursor_col: u16, layout_id: u64,) !void { return installStableAnchorWithOriginForTest( runtime, alloc, flow, selection, visual_offset, cursor_row, cursor_col, layout_id, null, );}
fn stableHistoryVisualOffsetForTest(runtime: *const TranscriptRuntime) !u32 { return switch (runtime.transcript_commit_state) { .stable => |anchor| anchor.history_visual_offset, .invalid, .recovering => error.TestExpectedStableTranscript, };}
fn expectStableNormalBufferRecoveryForTest( runtime: *const TranscriptRuntime, expected_history_visual_offset: u32,) !void { switch (runtime.transcript_commit_state) { .stable => |anchor| { try std.testing.expect(anchor.normal_buffer_recovery_pending); try std.testing.expectEqual( expected_history_visual_offset, anchor.history_visual_offset, ); }, .invalid, .recovering => return error.TestExpectedStableTranscript, }}
fn testPaintPlan( runtime: *const TranscriptRuntime, selection: viewport_selection.ViewportSelection,) render_engine.paint_plan.PaintPlan { const footer_top = runtime.layout.content_bottom + 1; return .{ .layout = runtime.layout, .viewport = selection, .footer = .{ .top = footer_top, .top_divider = footer_top, .banner = footer_top, .banner_active = false, .input_base = @min(footer_top + 1, runtime.layout.rows), .picker_divider = @min(footer_top + 2, runtime.layout.rows), .picker_start = runtime.layout.rows, .bottom_divider = @min(footer_top + 2, runtime.layout.rows), .hint = runtime.layout.rows, .total_rows = runtime.layout.rows - footer_top + 1, }, .activity = .none, .preserved_band = if (runtime.owned_top_row > 1) .{ .top = 1, .bottom = runtime.owned_top_row - 1, .owner = .preserved_shell } else render_engine.paint_plan.FrameBand.empty(.preserved_shell), .transcript_band = .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom, .owner = .transcript, }, .activity_band = render_engine.paint_plan.FrameBand.empty(.activity), .footer_band = .{ .top = footer_top, .bottom = runtime.layout.rows, .owner = .footer, }, .invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), .footer_clean_allowed = true, .synchronized_update = true, .cursor_target = .{ .row = selection.bottom_row, .col = 1, .visible = true }, .bottom_reserved_rows = 0, .preserve_scrollback = true, };}
const TestTranscriptFixedPointFrame = struct { layout: render_engine.frame_layout.FrameLayout, scroll_plan: render_engine.frame_scroll_plan.FrameScrollPlan,};
fn solveTestTranscriptFixedPointFrame( runtime: *TranscriptRuntime, alloc: Allocator, metrics: *Metrics, source: *const TranscriptPreparationSource, footer_rows: u16, prepared: *?transcript_painter.PreparedTranscriptSurfacePaint,) !TestTranscriptFixedPointFrame { var release_floor_rows: u16 = 0; var previous_candidate_owned_top: ?u16 = null;
while (true) { const release = render_engine.frame_layout.solveWithPreservedRowRelease(.{ .terminal = runtime.layout, .owned_top = runtime.owned_top_row, .footer = .{ .natural_rows = footer_rows, .min_rows = footer_rows, .max_rows = footer_rows, }, .transcript = source.preview, .prior = runtime.committed_frame_layout, }, release_floor_rows); const candidate = release.layout; if (previous_candidate_owned_top) |previous| { if (candidate.owned_top >= previous) return error.InvalidFrameScrollPlan; }
if (prepared.*) |*paint| { paint.deinit(alloc); prepared.* = null; } var inline_advance_rows: u16 = 0; if (!candidate.transcript_area.isEmpty()) { prepared.* = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, metrics, source, candidate.transcript_area, ); inline_advance_rows = runtime.planTranscriptScroll(&prepared.*.?).planned_rows; } const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, release.released_rows, inline_advance_rows, ); if (candidate.owned_top == scroll_plan.post_scroll_owned_top) { return .{ .layout = candidate, .scroll_plan = scroll_plan, }; } previous_candidate_owned_top = candidate.owned_top; release_floor_rows = scroll_plan.preserved_release_rows; }}
fn testPaintPlanForFrame( layout: render_engine.frame_layout.FrameLayout, selection: viewport_selection.ViewportSelection, cursor_row: u16, cursor_col: u16,) render_engine.paint_plan.PaintPlan { const footer_rows = render_engine.footer_layout.resolve(.{ .footer_top_for_extra = layout.footer_area.top, .terminal_rows = layout.terminal.rows, .activity_offset = 0, .extra_input_rows = 0, .input_extra = 0, .picker_rows = 0, .banner_active = false, .allocated_rows = layout.footer_area.height(), }); return layout.toPaintPlan(.{ .footer_rows = footer_rows, .viewport = selection, .cursor_target = .{ .row = cursor_row, .col = cursor_col, .visible = true, }, });}
fn testSource( alloc: Allocator, bytes: []const u8, cols: u16,) !TranscriptPreparationSource { return .{ .bytes = try alloc.dupe(u8, bytes), .folded_summary_indices = &.{}, .preview = .{ .natural_visual_rows = 4 }, .tail_kind = null, .tracked_entry_id = null, .tracked_entry_start_line = null, .replaceable_last_line = false, .replaceable_start = 0, .replaceable_row = 1, .welcome_cut_line = null, .welcome_boundary = null, .cols = cols, };}
fn testFoldedSource( alloc: Allocator, bytes: []const u8, cols: u16, summary_transcript_index: usize,) !TranscriptPreparationSource { var source = try testSource(alloc, bytes, cols); errdefer source.deinit(alloc); source.folded_summary_indices = try alloc.dupe( usize, &.{summary_transcript_index}, ); return source;}
fn testSourceWithReplaceableTail( alloc: Allocator, bytes: []const u8, cols: u16, replaceable_start: usize,) !TranscriptPreparationSource { var source = try testSource(alloc, bytes, cols); source.replaceable_last_line = true; source.replaceable_start = replaceable_start; return source;}
fn appendFoldedLineForTest( runtime: *TranscriptRuntime, alloc: Allocator, block_index: usize, text: []const u8,) !void { const owned = try alloc.dupe(u8, text); errdefer alloc.free(owned); try runtime.folded_command_blocks.items[block_index].lines.append(alloc, .{ .stream = .stdout, .text = owned, });}
fn prepareTestSourceForCurrentArea( runtime: *TranscriptRuntime, alloc: Allocator, source: *const TranscriptPreparationSource,) !transcript_painter.PreparedTranscriptSurfacePaint { var metrics: Metrics = .{}; return runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, source, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom, }, );}
fn preparedVisualOffsetForTest( prepared: *const transcript_painter.PreparedTranscriptSurfacePaint,) u32 { var offset: u32 = 0; const end = @min( prepared.selection.start_line, prepared.sourceVisualRows().len, ); for (prepared.sourceVisualRows()[0..end]) |rows| offset += rows; return offset + prepared.selection.partial_skip_rows;}
test "unchanged transcript keeps stable projection during footer reflow" { const alloc = std.testing.allocator; const flow = "line 00\n" ++ "line 01\n" ++ "line 02\n" ++ "line 03\n" ++ "line 04\n" ++ "line 05\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(60, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var metrics = Metrics{};
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var compact = try runtime.prepareTranscriptSurfacePaintFromSourceForFrame( alloc, &metrics, &source, .{ .top = 1, .bottom = 4 }, false, ); defer compact.deinit(alloc); try std.testing.expectEqual(@as(usize, 2), compact.selection.start_line); try installStableAnchorWithOriginForTest( &runtime, alloc, flow, compact.selection, preparedVisualOffsetForTest(&compact), compact.cursor.cursor_row, compact.cursor.cursor_col, 800, .{ .source_cols = runtime.layout.cols, .source_visual_offset = preparedVisualOffsetForTest(&compact), }, );
var expanded = try runtime.prepareTranscriptSurfacePaintFromSourceForFrame( alloc, &metrics, &source, .{ .top = 1, .bottom = 6 }, true, ); defer expanded.deinit(alloc);
try std.testing.expectEqual(compact.selection.start_line, expanded.selection.start_line); try std.testing.expectEqual(compact.selection.last_visible_row, expanded.selection.last_visible_row);}
test "structured user turn source keeps full prompt beyond byte cache cap" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 18, 14), .owned_top_row = 1, .max_transcript_bytes = 256, }; defer runtime.deinit(alloc);
const prompt = try makeLongPastePrompt(alloc, 512); try std.testing.expect(prompt.len > runtime.max_transcript_bytes); _ = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{}, }); const assistant_id = try runtime.appendAssistantTurnEntry(alloc); const assistant = runtime.lookupAssistantSegments(assistant_id) orelse unreachable; try assistant.text.appendSlice(alloc, "assistant after long prompt\n");
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
try std.testing.expect(source.bytes.len > runtime.max_transcript_bytes); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "LONG_PASTE_FIRST_MARKER") != null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "LONG_PASTE_LAST_MARKER") != null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "assistant after long prompt") != null);}
test "large current user turn survives assistant retention pass" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 18, 14), .owned_top_row = 1, .max_transcript_bytes = 256, .max_retained_transcript_bytes = 128, }; defer runtime.deinit(alloc);
const prompt = try makeLongPastePrompt(alloc, 64); const user_id = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{}, }); const assistant_id = try runtime.appendAssistantTurnEntry(alloc); const assistant = runtime.lookupAssistantSegments(assistant_id) orelse unreachable; try assistant.text.appendSlice(alloc, "assistant begins\n");
try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expectEqual(user_id, runtime.entries.items[0].id()); try std.testing.expectEqual(assistant_id, runtime.entries.items[1].id());
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "LONG_PASTE_FIRST_MARKER") != null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "assistant begins") != null);}
test "finalize transcript transition rejects frame inline row mismatch" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var source = try testSource(alloc, "line\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(u16, 0), runtime.planTranscriptScroll(&prepared).planned_rows);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, 1, ); var plan = testPaintPlan(&runtime, prepared.selection); try std.testing.expectError( error.InvalidFrameScrollPlan, resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ), );}
test "child full transcript finalizer consumes one complete transition" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try std.testing.expect( try runtime.setTranscriptPresentationDepth(alloc, .full), );
var source = try testSource(alloc, "child full transcript\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScrollForFrame(&prepared, false, false); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try runtime.finalizeTranscriptTransitionForFrame( alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, false, false, ); defer transition.deinit(alloc);
runtime.consumeFrameScrollCommit(alloc, scroll_plan, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = !transition.document_append.isEmpty(), .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }, &transition);
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expectEqual( transition.target_layout.layout_id, runtime.committed_frame_layout.layout_id, );}
fn commitPreparedForTest( runtime: *TranscriptRuntime, alloc: Allocator, source: *TranscriptPreparationSource, prepared: *transcript_painter.PreparedTranscriptSurfacePaint,) !void { const facts = runtime.planTranscriptScroll(prepared); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, source, prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = !transition.document_append.isEmpty(), .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });}
fn commitPreparedPartiallyForTest( runtime: *TranscriptRuntime, alloc: Allocator, source: *TranscriptPreparationSource, prepared: *transcript_painter.PreparedTranscriptSurfacePaint,) !void { const facts = runtime.planTranscriptScroll(prepared); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, source, prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = transition.materialized.cursor_row, .committed_cursor_col = transition.materialized.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });}
fn setPreparedVisualOffsetForTest( prepared: *transcript_painter.PreparedTranscriptSurfacePaint, target: u32,) !void { var remaining = target; var line_index: usize = 0; while (line_index < prepared.sourceVisualRows().len and remaining >= prepared.sourceVisualRows()[line_index]) : (line_index += 1) { remaining -= prepared.sourceVisualRows()[line_index]; } if (line_index >= prepared.sourceVisualRows().len) { return error.TestExpectedRepresentableVisualOffset; } prepared.selection.start_line = line_index; prepared.selection.partial_skip_rows = @intCast(remaining); prepared.selection.line_count = prepared.sourceVisualRows().len;}
const MeasuredEndpointMode = enum { seeded_exact, natural_null, seeded_inexact,};
fn expectZeroReverseRestoresSourceOrigin( runtime: *TranscriptRuntime, alloc: Allocator, wide_source: *TranscriptPreparationSource, narrow_source: *TranscriptPreparationSource, positive_delta: i32, endpoint_mode: MeasuredEndpointMode,) !void { var wide_prepared = try prepareTestSourceForCurrentArea( runtime, alloc, wide_source, ); defer wide_prepared.deinit(alloc); const source_origin = preparedVisualOffsetForTest(&wide_prepared); try std.testing.expect(source_origin > 0); try installStableAnchorForTest( runtime, alloc, wide_source.bytes, wide_prepared.selection, source_origin, wide_prepared.cursor.cursor_row, wide_prepared.cursor.cursor_col, 700, );
runtime.layout = transcriptTestLayout(narrow_source.cols, 8, 4); runtime.resize_history_row_delta = positive_delta; var narrow_prepared = try prepareTestSourceForCurrentArea( runtime, alloc, narrow_source, ); defer narrow_prepared.deinit(alloc); const natural_endpoint = if (narrow_prepared.measured_history_origin) |origin| origin.endpoint else null; if (endpoint_mode == .seeded_inexact) { try std.testing.expect(natural_endpoint == null); } if (endpoint_mode != .natural_null) { narrow_prepared.measured_history_origin = .{ .source_cols = wide_source.cols, .source_visual_offset = source_origin, .endpoint = .{ .span_start_flow_offset = 0, .flow_offset = 0, .hard_row_cols = wide_source.cols, }, }; } try std.testing.expectEqual( endpoint_mode != .natural_null, if (narrow_prepared.measured_history_origin) |origin| origin.endpoint != null else false, ); const narrow_origin = preparedVisualOffsetForTest(&narrow_prepared); try std.testing.expect(narrow_origin > source_origin); try commitPreparedForTest(runtime, alloc, narrow_source, &narrow_prepared); const narrowed = runtime.stableTranscriptProjectionForFlow( wide_source.bytes, ) orelse return error.TestExpectedStableTranscript; const retained_origin = narrowed.measured_history_origin orelse return error.TestExpectedMeasuredHistoryOrigin; try std.testing.expectEqual(source_origin, retained_origin.source_visual_offset); try std.testing.expectEqual(wide_source.cols, retained_origin.source_cols); try std.testing.expectEqual( endpoint_mode != .natural_null, retained_origin.endpoint != null, );
runtime.layout = transcriptTestLayout(wide_source.cols, 12, 8); runtime.resize_history_row_delta = 0; var restored = try prepareTestSourceForCurrentArea( runtime, alloc, wide_source, ); defer restored.deinit(alloc); try std.testing.expectEqual( source_origin, preparedVisualOffsetForTest(&restored), );}
fn totalVisualRowsForTest(line_rows: []const u16) u32 { var total: u32 = 0; for (line_rows) |rows| total += rows; return total;}
test "semantic row identity remains exact beyond 65535 rows" { const alloc = std.testing.allocator; const row_count: usize = 65_540; var flow_writer: std.Io.Writer.Allocating = .init(alloc); defer flow_writer.deinit(); for (0..row_count) |index| { if (index > 0) try flow_writer.writer.writeByte('\n'); try flow_writer.writer.print("semantic-row-{d}", .{index}); } const flow = flow_writer.written();
var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc);
try std.testing.expectEqual(row_count, prepared.sourceVisualRows().len); try std.testing.expectEqual( @as(u32, @intCast(row_count)), totalVisualRowsForTest(prepared.sourceVisualRows()), ); try std.testing.expectEqual(row_count - 8, prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 0), prepared.selection.partial_skip_rows);
const boundary_indices = [_]usize{ 65_534, 65_535, 65_536, row_count - 1 }; for (boundary_indices) |index| { var marker_buf: [32]u8 = undefined; const marker = try std.fmt.bufPrint( &marker_buf, "semantic-row-{d}", .{index}, ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, flow, marker), ); try std.testing.expectEqualStrings( marker, resolveVisibleLineForTest( prepared.visible_lines.items[index], .{ .bytes = prepared.bytes }, ), ); }
try commitPreparedForTest(&runtime, alloc, &source, &prepared); const stable = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestExpectedStableTranscript; try std.testing.expectEqual(row_count - 8, stable.selection.start_line); try std.testing.expectEqual( @as(u32, @intCast(row_count - 8)), stable.visual_offset, ); try std.testing.expectEqual( @as(u32, @intCast(row_count)), stable.total_visual_rows, );}
fn makeSyntheticPreparedRenderable( prepared: *transcript_painter.PreparedTranscriptSurfacePaint, alloc: Allocator,) !void { const line_count = prepared.line_visual_rows.items.len; try prepared.visible_lines.ensureTotalCapacity(alloc, line_count); while (prepared.visible_lines.items.len < line_count) { prepared.visible_lines.appendAssumeCapacity(.{ .transcript = .{ .ref = .{ .ref = .{ .start = 0, .len = 0 }, }, }, }); } prepared.selection.line_count = line_count; prepared.total_lines = line_count;}
fn enterRendererDerivedSemanticRecovery( runtime: *TranscriptRuntime, alloc: Allocator, flow: []const u8, attempt_content_bottom: u16, accepted_semantic_rows: u16, replaceable_start: ?usize,) !u32 { var stable_source = if (replaceable_start) |start| try testSourceWithReplaceableTail(alloc, flow, runtime.layout.cols, start) else try testSource(alloc, flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), stable_prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 0), stable_prepared.selection.partial_skip_rows); try installStableAnchorForTest( runtime, alloc, flow, stable_prepared.selection, 0, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 91, );
runtime.layout = transcriptTestLayout( runtime.layout.cols, runtime.layout.rows, attempt_content_bottom, ); var attempt_source = if (replaceable_start) |start| try testSourceWithReplaceableTail(alloc, flow, runtime.layout.cols, start) else try testSource(alloc, flow, runtime.layout.cols); defer attempt_source.deinit(alloc); var attempt_prepared = try prepareTestSourceForCurrentArea( runtime, alloc, &attempt_source, ); defer attempt_prepared.deinit(alloc);
const attempt_facts = runtime.planTranscriptScroll(&attempt_prepared); try std.testing.expect(attempt_facts.semantic_rows > accepted_semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, attempt_facts.planned_rows, ); var plan = testPaintPlan(runtime, attempt_prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &attempt_source, &attempt_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = if (accepted_semantic_rows > 0) 1 else 0, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = accepted_semantic_rows, .document_append_committed = false, .committed_cursor_row = attempt_prepared.cursor.cursor_row, .committed_cursor_col = attempt_prepared.cursor.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, ); return attempt_facts.target_visual_offset;}
fn expectFinalizedRecoveryProjectionPaintsCoherently( runtime: *TranscriptRuntime, alloc: Allocator, plan: render_engine.paint_plan.PaintPlan, prepared: *const transcript_painter.PreparedTranscriptSurfacePaint, transition: *const transcript_runtime.TranscriptTransition, expected_first_row_text: ?[]const u8,) !void { var shadow = try vt_emulator.Grid.init(alloc, plan.layout.cols, plan.layout.rows); defer shadow.deinit(); var surface = try render_engine.frame_surface.FrameSurface.initFromShadow( alloc, plan, shadow, ); defer surface.deinit();
const result = try runtime.paintPreparedTranscriptIntoSurface( alloc, &surface, prepared, ); try std.testing.expectEqualDeep(transition.selection, surface.plan.viewport); try std.testing.expectEqual( transition.selection.last_visible_row, result.last_row, ); try std.testing.expectEqual( transition.selection.replaceable_start_row, result.replaceable_start_row, ); try std.testing.expectEqual( transition.cursor_row, surface.cursor_target.?.row, ); try std.testing.expectEqual( transition.cursor_col, surface.cursor_target.?.col, ); try std.testing.expectEqual( transition.replaceable_row, prepared.cursor.replaceable_row, );
if (expected_first_row_text) |expected| { var target = try surface.copyToTargetGrid(alloc); defer target.deinit(); var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); try target.rowText(plan.viewport.top_row, &row_text); try std.testing.expect(std.mem.startsWith(u8, row_text.items, expected)); } if (transition.selection.last_visible_row_blank) { var col: u16 = 1; while (col <= surface.cols) : (col += 1) { const cell = surface.cellAt( transition.selection.last_visible_row, col, ).?; try std.testing.expectEqual(@as(u21, ' '), cell.codepoint); } }}
const OwnedBandInvalidationProbe = struct { saw_owned_band_change: bool = false,
fn paint(ctx: *anyopaque, surface: *render_engine.frame_surface.FrameSurface) anyerror!void { const self: *OwnedBandInvalidationProbe = @ptrCast(@alignCast(ctx)); for (surface.plan.invalidation.ranges()) |range| { if (range.reason == .owned_band_change) { self.saw_owned_band_change = true; } } }};
const PreparedTranscriptFramePainter = struct { runtime: *TranscriptRuntime, alloc: Allocator, prepared: *const transcript_painter.PreparedTranscriptSurfacePaint,
fn paint(ctx: *anyopaque, surface: *render_engine.frame_surface.FrameSurface) anyerror!void { const self: *PreparedTranscriptFramePainter = @ptrCast(@alignCast(ctx)); _ = try self.runtime.paintPreparedTranscriptIntoSurface( self.alloc, surface, self.prepared, ); }};
fn appendGridRows( alloc: Allocator, grid: *const vt_emulator.Grid, first_row: u16, row_count: u16, history: *std.ArrayList(u8), row_text: *std.ArrayList(u8),) !void { var row_offset: u16 = 0; while (row_offset < row_count) : (row_offset += 1) { row_text.clearRetainingCapacity(); try grid.rowTextTrimmed(first_row + row_offset, row_text); try std.testing.expect(row_text.items.len > 0); if (history.items.len > 0) try history.append(alloc, '\n'); try history.appendSlice(alloc, row_text.items); }}
fn countExactHistoryLine(history: []const u8, expected: []const u8) usize { var count: usize = 0; var lines = std.mem.splitScalar(u8, history, '\n'); while (lines.next()) |line| { if (std.mem.eql(u8, line, expected)) count += 1; } return count;}
fn cloneRecoveryTransitionForTest( alloc: Allocator, source: *const transcript_runtime.TranscriptTransition,) !transcript_runtime.TranscriptTransition { std.debug.assert(!source.consumed); std.debug.assert(source.document_append.isEmpty()); std.debug.assert(source.document_append_bytes.len == 0);
var clone = source.*; clone.document_append.bytes = &.{}; clone.document_append_bytes = &.{}; clone.target_flow = &.{}; clone.target_cache = .empty; clone.folded_summary_indices = &.{}; errdefer clone.deinit(alloc);
clone.target_flow = try alloc.dupe(u8, source.target_flow); try clone.target_cache.appendSlice(alloc, source.target_cache.items); clone.folded_summary_indices = try alloc.dupe( usize, source.folded_summary_indices, ); return clone;}
fn createProductionCappedFoldedRecoveryTransition( alloc: Allocator, cols: u16, viewport_rows: u16,) !transcript_runtime.TranscriptTransition { const stable_flow = "summary"; const initial_owned_top = viewport_rows + 1; const content_bottom = initial_owned_top + viewport_rows - 1; const raw_line_count: usize = 3; const wrapped_rows_per_line: usize = (@as(usize, std.math.maxInt(u16)) + 1) / raw_line_count + 1; const raw_line_bytes = wrapped_rows_per_line * @as(usize, cols);
var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile( std.testing.io, "production-folded-capped-attempt.log", .{ .read = true }, ); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = transcriptTestLayout( cols, content_bottom + 4, content_bottom, ), .owned_top_row = initial_owned_top, .max_transcript_bytes = 2 * 1024 * 1024, .max_retained_transcript_bytes = 2 * 1024 * 1024, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.shadow_vt.?.feed("shell0\r\nshell1\r\nshell2\r\nshell3");
try runtime.transcript.appendSlice(alloc, stable_flow); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 1, }); var fold_index: usize = 0; while (fold_index < viewport_rows) : (fold_index += 1) { var fold_buf: [16]u8 = undefined; const fold = try std.fmt.bufPrint(&fold_buf, "fold{d}", .{fold_index}); try appendFoldedLineForTest(&runtime, alloc, 0, fold); }
var metrics: Metrics = .{}; var stable_source = try runtime.prepareTranscriptSource(alloc, null); defer stable_source.deinit(alloc); try std.testing.expectEqualStrings(stable_flow, stable_source.bytes); try std.testing.expectEqual(@as(usize, 1), stable_source.folded_summary_indices.len); try std.testing.expectEqual(@as(usize, 1), stable_source.folded_summary_indices[0]); try std.testing.expectEqual(viewport_rows, stable_source.preview.natural_visual_rows);
var stable_prepared: ?transcript_painter.PreparedTranscriptSurfacePaint = null; defer if (stable_prepared) |*paint| paint.deinit(alloc); const stable_frame = try solveTestTranscriptFixedPointFrame( &runtime, alloc, &metrics, &stable_source, 4, &stable_prepared, ); const stable_paint = &stable_prepared.?; try std.testing.expectEqual( stable_frame.layout.owned_top, stable_frame.scroll_plan.post_scroll_owned_top, ); var stable_plan = testPaintPlanForFrame( stable_frame.layout, stable_paint.selection, stable_paint.cursor.cursor_row, stable_paint.cursor.cursor_col, ); var stable_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &stable_source, stable_paint, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(stable_frame.layout), &stable_plan, stable_frame.scroll_plan, ); defer stable_transition.deinit(alloc); try std.testing.expect(stable_transition.document_append.isEmpty());
var stable_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = stable_paint, }; const stable_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = stable_plan, .scroll_plan = stable_frame.scroll_plan, .document_append = stable_transition.document_append, .body_painter = .{ .ctx = &stable_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, stable_result.shadow_state, ); runtime.ackPreservedRowReleaseAssumeValid( stable_frame.scroll_plan, stable_result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &stable_transition, stable_result); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
var raw_line: std.ArrayList(u8) = .empty; defer raw_line.deinit(alloc); var raw_line_index: usize = 0; while (raw_line_index < raw_line_count) : (raw_line_index += 1) { try runtime.transcript.appendSlice(alloc, "\n"); raw_line.clearRetainingCapacity(); try raw_line.appendNTimes( alloc, 'a' + @as(u8, @intCast(raw_line_index)), raw_line_bytes, ); try std.testing.expectEqual( @as(u16, @intCast(wrapped_rows_per_line)), visualRowsForLine(raw_line.items, cols), ); try runtime.transcript.appendSlice(alloc, raw_line.items); }
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual( @as(u16, std.math.maxInt(u16)), source.preview.natural_visual_rows, ); try std.testing.expectEqual(@as(usize, 1), source.folded_summary_indices.len); try std.testing.expectEqual(@as(usize, 1), source.folded_summary_indices[0]);
var prepared: ?transcript_painter.PreparedTranscriptSurfacePaint = null; defer if (prepared) |*paint| paint.deinit(alloc); const frame = try solveTestTranscriptFixedPointFrame( &runtime, alloc, &metrics, &source, 8, &prepared, ); const paint = &prepared.?; const facts = runtime.planTranscriptScroll(paint); try std.testing.expect(facts.semantic_rows > std.math.maxInt(u16)); try std.testing.expectEqual(viewport_rows, facts.planned_rows); try std.testing.expectEqual( frame.layout.owned_top, frame.scroll_plan.post_scroll_owned_top, ); try std.testing.expectEqual( viewport_rows, frame.scroll_plan.acceptedInlineRows( frame.scroll_plan.terminal_scroll_rows, ), );
var plan = testPaintPlanForFrame( frame.layout, paint.selection, paint.cursor.cursor_row, paint.cursor.cursor_col, ); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, paint, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(frame.layout), &plan, frame.scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty()); try std.testing.expectEqual(@as(?usize, null), transition.materialized.flow_len); try std.testing.expectEqual(viewport_rows, transition.materialized.visual_rows); try std.testing.expectEqual( @as(u32, viewport_rows), transition.materialized.visual_offset, ); var transition_template = try cloneRecoveryTransitionForTest( alloc, &transition, ); errdefer transition_template.deinit(alloc);
var painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = paint, }; const result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = plan, .scroll_plan = frame.scroll_plan, .document_append = transition.document_append, .body_painter = .{ .ctx = &painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, result.shadow_state, ); try std.testing.expectEqual( frame.scroll_plan.terminal_scroll_rows, result.terminal_scroll_rows_committed, ); try std.testing.expect(!result.document_append_committed); runtime.ackPreservedRowReleaseAssumeValid( frame.scroll_plan, result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &transition, result);
const diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, diagnostic.state, ); try std.testing.expectEqual(transition.visual_offset, diagnostic.visual_offset); try std.testing.expect(diagnostic.remaining_inline_rows > 0); try std.testing.expectEqual( facts.semantic_rows - facts.planned_rows, diagnostic.remaining_inline_rows, );
return transition_template;}
fn testSelectionInArea( start_line: usize, top_row: u16, bottom_row: u16,) viewport_selection.ViewportSelection { var selection = testSelection(start_line); selection.top_row = top_row; selection.bottom_row = bottom_row; return selection;}
fn enterSemanticAppendRecovery( runtime: *TranscriptRuntime, alloc: Allocator, stable_flow: []const u8, attempt_flow: []const u8, attempt_visual_rows: []const u16,) !void { const stable_selection = testSelectionInArea( 0, runtime.owned_top_row, runtime.layout.content_bottom, ); try installStableAnchorForTest( runtime, alloc, stable_flow, stable_selection, 0, runtime.layout.content_bottom, 1, 45, );
var source = try testSource(alloc, attempt_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelectionInArea( 4, runtime.owned_top_row, runtime.layout.content_bottom, ), .cursor = .{ .cursor_row = runtime.layout.content_bottom, .cursor_col = 1, .replaceable_row = runtime.layout.content_bottom, }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, attempt_visual_rows); try makeSyntheticPreparedRenderable(&prepared, alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 0), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 4), facts.semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 3, facts.planned_rows, ); try std.testing.expectEqual(@as(u16, 1), scroll_plan.remaining_inline_advance_rows);
var plan = testPaintPlan(runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty());
runtime.ackPreservedRowReleaseAssumeValid( scroll_plan, scroll_plan.terminal_scroll_rows, ); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = false, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = runtime.layout.content_bottom, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u32, 3), recovery.visual_offset); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);}
fn consumeUnacceptedSemanticRecoveryRetry( runtime: *TranscriptRuntime, alloc: Allocator, flow: []const u8, visual_rows: []const u16, start_line: usize,) !void { var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelectionInArea( start_line, runtime.owned_top_row, runtime.layout.content_bottom, ), .cursor = .{ .cursor_row = runtime.layout.content_bottom, .cursor_col = 1, .replaceable_row = runtime.layout.content_bottom, }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, visual_rows); try makeSyntheticPreparedRenderable(&prepared, alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 0), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = runtime.layout.content_bottom, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u32, 3), recovery.visual_offset); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);}
fn enterResizeReflowRecovery( runtime: *TranscriptRuntime, alloc: Allocator, resize_history_row_delta: ?i32, shadow_state: render_engine.terminal_diff.ShadowCommitState,) !void { return enterResizeReflowRecoveryWithAcceptedRows( runtime, alloc, resize_history_row_delta, shadow_state, 1, );}
fn enterResizeReflowRecoveryWithAcceptedRows( runtime: *TranscriptRuntime, alloc: Allocator, resize_history_row_delta: ?i32, shadow_state: render_engine.terminal_diff.ShadowCommitState, accepted_rows: u16,) !void { const flow = "stable\nbase\n"; try runtime.enableShadowVt(alloc); try installStableAnchorForTest( runtime, alloc, flow, testSelectionInArea(0, 1, 4), 0, 4, 1, 46, ); const shadow = runtime.shadow_vt.?; shadow.cells[19].codepoint = 'x'; shadow.cursor_row = 8; shadow.cursor_col = 1;
runtime.viewport_clear_pending = true; runtime.layout = transcriptTestLayout(10, 8, 2); runtime.resize_history_row_delta = resize_history_row_delta;
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 2), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), facts.semantic_rows); try std.testing.expect(accepted_rows < facts.planned_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = accepted_rows, .document_append_committed = false, .committed_cursor_row = 2, .committed_cursor_col = 1, .shadow_state = shadow_state, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(facts.target_visual_offset, recovery.visual_offset); try std.testing.expectEqual( facts.planned_rows - accepted_rows, recovery.remaining_inline_rows, );}
test "committed resize deferral installs target layout before recovery" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
try enterResizeReflowRecoveryWithAcceptedRows( &runtime, alloc, null, .committed, 0, );
const expected_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( testPaintPlan(&runtime, testSelectionInArea(0, 1, 2)), ); try std.testing.expectEqualDeep(expected_layout, runtime.committed_frame_layout); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, );}
const RecoveryAttemptOutcome = struct { facts: transcript_runtime.TranscriptScrollFacts, visual_offset: u32,};
fn consumeRendererRecoveryAttempt( runtime: *TranscriptRuntime, alloc: Allocator, flow: []const u8, committed_inline_rows: u16, shadow_state: render_engine.terminal_diff.ShadowCommitState,) !RecoveryAttemptOutcome { var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); try std.testing.expect(committed_inline_rows <= scroll_plan.terminal_scroll_rows); var plan = testPaintPlan(runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); const visual_offset = transition.visual_offset;
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = committed_inline_rows, .document_append_committed = !transition.document_append.isEmpty(), .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = shadow_state, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); return .{ .facts = facts, .visual_offset = visual_offset, };}
fn expectDocumentWireEqualsLogical( alloc: Allocator, logical: []const u8, wire: []const u8,) !void { var decoded: std.ArrayList(u8) = .empty; defer decoded.deinit(alloc); var index: usize = 0; while (index < wire.len) { if (wire[index] == '\n') return error.TestExpectedCarriageReturn; if (wire[index] == '\r' and index + 1 < wire.len and wire[index + 1] == '\n') { try decoded.append(alloc, '\n'); index += 2; } else { try decoded.append(alloc, wire[index]); index += 1; } } try std.testing.expectEqualStrings(logical, decoded.items);}
fn expectRendererAppendRestored( runtime: *TranscriptRuntime, alloc: Allocator, stable_flow: []const u8, grown_flow: []const u8,) !void { var source = try testSource(alloc, grown_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(!facts.recovery_rebase); try std.testing.expect(facts.semantic_rows > 0); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[stable_flow.len..], transition.document_append.bytes, );}
fn checkTranscriptTransitionStagingAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest(&runtime, alloc, "old\n", testSelection(0), 0, 3, 1, 71); const committed_before = runtime.transcriptCommitDiagnostic();
var source = try testSource(alloc, "old\nnew\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelection(1), .cursor = .{ .cursor_row = 4, .cursor_col = 1, .replaceable_row = 4 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &.{ 1, 1 }); const scroll_plan = render_engine.frame_scroll_plan.merge(runtime.layout.rows, 1, 0, 1); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ) catch |err| { try std.testing.expectEqualDeep(committed_before, runtime.transcriptCommitDiagnostic()); try std.testing.expect(runtime.stableTranscriptProjectionForFlow("old\n") != null); return err; }; defer transition.deinit(alloc);
try std.testing.expectEqualDeep(committed_before, runtime.transcriptCommitDiagnostic()); try std.testing.expect(runtime.stableTranscriptProjectionForFlow("old\n") != null);}
fn commandOutputTestStyles() Styles { return .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "", .dim_style = "", .red_style = "", };}
fn commandOutputAnsiTestStyles() Styles { return .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "\x1b[0m", .dim_style = "\x1b[2m", .red_style = "\x1b[31m", };}
fn semanticNoticePaletteA() Styles { return .{ .system_notice_text_style = "\x1b[37m", .reset_style = "\x1b[0m", .notice_information_style = "\x1b[36m", .notice_success_style = "\x1b[32m", .notice_warning_style = "\x1b[33m", .notice_error_style = "\x1b[31m", .notice_cancelled_style = "\x1b[90m", };}
fn semanticNoticePaletteB() Styles { return .{ .system_notice_text_style = "\x1b[35m", .reset_style = "\x1b[0m", .notice_information_style = "\x1b[34m", .notice_success_style = "\x1b[92m", .notice_warning_style = "\x1b[93m", .notice_error_style = "\x1b[91m", .notice_cancelled_style = "\x1b[2m", };}
test "semantic notice append and replacement preserve ownership identity and timestamp" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(32, 16, 12) }; defer runtime.deinit(alloc); runtime.setCommandOutputRenderPolicy(semanticNoticePaletteA());
const entry_id = try runtime.appendReplaceableSemanticNotice(alloc, .{ .topic = "build", .tone = .warning, .body = "first body", }); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .semantic_notice); try std.testing.expect(runtime.entries.items[0].semantic_notice.pending_replacement); const created_at_ms = runtime.entries.items[0].createdAtMs(); try std.testing.expectEqualStrings("build", runtime.entries.items[0].semantic_notice.topic); try std.testing.expectEqualStrings("first body", runtime.entries.items[0].semantic_notice.body);
try std.testing.expect(try runtime.replaceSemanticNotice(alloc, entry_id, .{ .topic = "deploy", .tone = .success, .body = "replacement body with /tmp/output/path", .visibility = .full_only, })); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); try std.testing.expectEqual(created_at_ms, runtime.entries.items[0].createdAtMs()); try std.testing.expectEqualStrings("deploy", runtime.entries.items[0].semantic_notice.topic); try std.testing.expectEqual(types.NoticeTone.success, runtime.entries.items[0].semantic_notice.tone); try std.testing.expectEqualStrings("replacement body with /tmp/output/path", runtime.entries.items[0].semantic_notice.body); try std.testing.expectEqual(types.NoticeVisibility.full_only, runtime.entries.items[0].semantic_notice.visibility); try std.testing.expectEqualStrings("", runtime.transcript.items); // The finished replacement clears the pending-replacement pin, making // the entry immutable history: a second in-place replacement is refused. try std.testing.expect(!runtime.entries.items[0].semantic_notice.pending_replacement); try std.testing.expectError( error.MissingNoticeReplacementPin, runtime.replaceSemanticNotice(alloc, entry_id, .{ .topic = "again", .tone = .information, .body = "must not apply", }), ); try std.testing.expect(!(try runtime.replaceSemanticNotice(alloc, entry_id + 1, .{ .topic = "missing", .tone = .information, .body = "unchanged", })));}
fn checkSemanticNoticeAppendAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(32, 16, 12) }; defer runtime.deinit(alloc); const entry_id = try runtime.appendSemanticNotice(alloc, .{ .topic = "allocator", .tone = .@"error", .body = "owned topic and body", }); try std.testing.expectEqual(@as(u32, 1), entry_id); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqualStrings("allocator", runtime.entries.items[0].semantic_notice.topic); try std.testing.expectEqualStrings("owned topic and body", runtime.entries.items[0].semantic_notice.body);}
test "semantic notice append is allocator safe" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkSemanticNoticeAppendAllocationFailures, .{}, );}
fn checkSemanticNoticeReplacementAllocationFailures(alloc: Allocator) !void { const base = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(32, 16, 12) }; defer runtime.deinit(base); const entry_id = try runtime.appendReplaceableSemanticNotice(base, .{ .topic = "before", .tone = .information, .body = "stable body", }); const created_at_ms = runtime.entries.items[0].createdAtMs(); const next_entry_id = runtime.next_entry_id;
const replaced = runtime.replaceSemanticNotice(alloc, entry_id, .{ .topic = "after", .tone = .cancelled, .body = "replacement body", }) catch |err| { try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); try std.testing.expectEqual(created_at_ms, runtime.entries.items[0].createdAtMs()); try std.testing.expectEqualStrings("before", runtime.entries.items[0].semantic_notice.topic); try std.testing.expectEqualStrings("stable body", runtime.entries.items[0].semantic_notice.body); try std.testing.expectEqual(next_entry_id, runtime.next_entry_id); return err; }; try std.testing.expect(replaced); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); try std.testing.expectEqual(created_at_ms, runtime.entries.items[0].createdAtMs()); try std.testing.expectEqualStrings("after", runtime.entries.items[0].semantic_notice.topic); try std.testing.expectEqualStrings("replacement body", runtime.entries.items[0].semantic_notice.body); try std.testing.expectEqual(next_entry_id, runtime.next_entry_id);}
test "semantic notice replacement is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkSemanticNoticeReplacementAllocationFailures, .{}, );}
test "one retained semantic notice reflows through compact and full projection with retained palettes" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(36, 24, 20) }; defer runtime.deinit(alloc); const notice: types.SemanticNotice = .{ .topic = "paths", .tone = .information, .body = "review /workspace/packages/terminal/rendering.zig before continuing", }; const entry_id = try runtime.appendSemanticNotice(alloc, notice);
const cases = [_]struct { cols: u16, styles: Styles }{ .{ .cols = 36, .styles = semanticNoticePaletteA() }, .{ .cols = 18, .styles = semanticNoticePaletteB() }, }; for (cases) |case| { runtime.layout = transcriptTestLayout(case.cols, 24, 20); runtime.setCommandOutputRenderPolicy(case.styles); const expected = try transcript_blocks.renderSemanticNotice(alloc, notice, case.styles, case.cols); defer alloc.free(expected);
var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); try std.testing.expect(std.mem.find(u8, compact.bytes, expected) != null);
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, case.cols, 64, 0, null, ); defer alloc.free(full); try std.testing.expect(std.mem.find(u8, full, expected) != null); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); }}
test "visible overflow after committed frame leaves scroll planning to viewport selection" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, .viewport_top_row = 4, .owned_top_row = 4, .cursor_row = 8, .cursor_col = 1, .has_painted_transcript = true, .has_committed_frame = true, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.writeTranscript(alloc, &metrics, "overflow\n", true);
try std.testing.expectEqual(@as(u16, 4), runtime.owned_top_row); try std.testing.expectEqual(@as(u16, 4), runtime.viewport_top_row);}
test "neutral transcript flow preview reports natural rows without footer reservation" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, .viewport_top_row = 1, .owned_top_row = 1, .cursor_row = 8, .cursor_col = 1, .has_painted_transcript = true, .last_paint_bottom_reserved_rows = 3, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.transcript.appendSlice(alloc, "line 1\nline 2\nline 3\n");
const preview = try runtime.previewTranscriptFlow(alloc);
try std.testing.expectEqual(@as(u16, 3), preview.natural_visual_rows); try std.testing.expectEqual(@as(u16, 1), preview.cursor_col); try std.testing.expectEqual(@as(u16, 3), runtime.last_paint_bottom_reserved_rows);}
test "stable transcript planning uses visual rows and partial skips" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest(&runtime, alloc, "stable", testSelection(0), 0, 3, 1, 41);
var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = try alloc.dupe(u8, "stable"), .selection = testSelection(1), }; defer prepared.deinit(alloc); prepared.selection.partial_skip_rows = 2; try prepared.line_visual_rows.appendSlice(alloc, &.{ 3, 4, 2, 1 });
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u32, 5), facts.target_visual_offset); try std.testing.expectEqual(@as(u16, 5), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 5), facts.planned_rows);}
test "compatible growth beyond one frame retains exact semantic debt" { const alloc = std.testing.allocator; const stable_flow = "base\n"; const extra_row_count: usize = @as(usize, std.math.maxInt(u16)) + 8; var grown_flow: std.ArrayList(u8) = .empty; defer grown_flow.deinit(alloc); try grown_flow.ensureTotalCapacity( alloc, stable_flow.len + extra_row_count * "x\n".len, ); grown_flow.appendSliceAssumeCapacity(stable_flow); for (0..extra_row_count) |_| { grown_flow.appendSliceAssumeCapacity("x\n"); }
var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, stable_flow, testSelection(0), 0, 2, 1, 411, );
var probe_source = try testSource(alloc, grown_flow.items, runtime.layout.cols); defer probe_source.deinit(alloc); var probe_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &probe_source, ); defer probe_prepared.deinit(alloc); const probe_facts = runtime.planTranscriptScroll(&probe_prepared); const exact_semantic_rows = probe_facts.target_visual_offset; try std.testing.expect(exact_semantic_rows > std.math.maxInt(u16)); try std.testing.expectEqual( exact_semantic_rows, @as(u32, probe_facts.semantic_rows), ); try std.testing.expectEqual( std.math.maxInt(u16), probe_facts.planned_rows, );
const accepted_rows = std.math.maxInt(u16); const initial = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow.items, accepted_rows, .committed, ); try std.testing.expectEqual( exact_semantic_rows, @as(u32, initial.facts.semantic_rows), ); const remaining_after_cap = exact_semantic_rows - accepted_rows; try std.testing.expectEqual( remaining_after_cap, @as(u32, runtime.transcriptCommitDiagnostic().remaining_inline_rows), ); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty); runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const repeated = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow.items, 1, .terminal_partial_write, ); try std.testing.expectEqual( remaining_after_cap, @as(u32, repeated.facts.semantic_rows), ); const remaining_semantic_rows = remaining_after_cap - 1; try std.testing.expectEqual( remaining_semantic_rows, @as(u32, runtime.transcriptCommitDiagnostic().remaining_inline_rows), );
const complete = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow.items, @intCast(remaining_semantic_rows), .committed, ); try std.testing.expectEqual( remaining_semantic_rows, @as(u32, complete.facts.semantic_rows), ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const stable = runtime.stableTranscriptProjectionForFlow(grown_flow.items) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(exact_semantic_rows, stable.visual_offset);}
test "oversized compatible growth materializes only each accepted projection" { const alloc = std.testing.allocator; const stable_flow = "base\n"; const filler_row_count: usize = @as(usize, std.math.maxInt(u16)) - 1; const staged_tail = "cap 0\n" ++ "cap 1\n" ++ "cap 2\n" ++ "cap 3\n" ++ "gap\n" ++ "tail 0\n" ++ "tail 1\n" ++ "tail 2\n" ++ "tail 3"; var grown_flow: std.ArrayList(u8) = .empty; defer grown_flow.deinit(alloc); try grown_flow.ensureTotalCapacity( alloc, stable_flow.len + filler_row_count * "x\n".len + staged_tail.len, ); grown_flow.appendSliceAssumeCapacity(stable_flow); for (0..filler_row_count) |_| { grown_flow.appendSliceAssumeCapacity("x\n"); } grown_flow.appendSliceAssumeCapacity(staged_tail);
var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile( std.testing.io, "oversized-staged-projection.log", .{ .read = true }, ); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.shadow_vt.?.feed(stable_flow);
var stable_source = try testSource(alloc, stable_flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, stable_flow, stable_prepared.selection, 0, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 412, );
var materialized_append: std.ArrayList(u8) = .empty; defer materialized_append.deinit(alloc);
var first_source = try testSource(alloc, grown_flow.items, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expectEqual( @as(u16, std.math.maxInt(u16)), first_facts.planned_rows, ); try std.testing.expect(first_facts.semantic_rows > first_facts.planned_rows); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc);
try std.testing.expectEqual( @as(u32, std.math.maxInt(u16)), first_transition.visual_offset, ); try std.testing.expectEqual( first_facts.target_visual_offset + 4, first_transition.total_visual_rows, ); try std.testing.expectEqual(@as(u16, 5), first_transition.cursor_row); try std.testing.expectEqual(@as(u16, 1), first_transition.cursor_col); try std.testing.expect(!first_transition.document_append.isEmpty()); try std.testing.expect(first_transition.materialized.flow_len.? < grown_flow.items.len); try materialized_append.appendSlice( alloc, first_transition.document_append.bytes, );
var first_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = &first_prepared, }; var metrics: Metrics = .{}; const first_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = first_plan, .scroll_plan = first_scroll_plan, .document_append = first_transition.document_append, .body_painter = .{ .ctx = &first_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, first_result.shadow_state, ); var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); try runtime.shadow_vt.?.rowTextTrimmed(1, &row_text); try std.testing.expectEqualStrings("cap 0", row_text.items); row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(4, &row_text); try std.testing.expectEqualStrings("cap 3", row_text.items);
runtime.consumeTranscriptTransition(alloc, &first_transition, first_result); const after_first = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, after_first.state, ); try std.testing.expectEqual( first_facts.semantic_rows - first_facts.planned_rows, after_first.remaining_inline_rows, ); try std.testing.expectEqual( @as(u32, std.math.maxInt(u16)), after_first.visual_offset, );
var retry_source = try testSource(alloc, grown_flow.items, runtime.layout.cols); defer retry_source.deinit(alloc); var retry_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &retry_source, ); defer retry_prepared.deinit(alloc); const retry_facts = runtime.planTranscriptScroll(&retry_prepared); try std.testing.expectEqual( after_first.remaining_inline_rows, retry_facts.semantic_rows, ); const retry_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, retry_facts.planned_rows, ); var retry_plan = testPaintPlan(&runtime, retry_prepared.selection); var retry_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &retry_source, &retry_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(retry_plan), &retry_plan, retry_scroll_plan, ); defer retry_transition.deinit(alloc); try std.testing.expectEqual( retry_facts.target_visual_offset, retry_transition.visual_offset, ); try std.testing.expect(!retry_transition.document_append.isEmpty()); try std.testing.expectEqual( first_transition.cursor_row, retry_transition.document_append.start_row, ); try std.testing.expectEqual( first_transition.cursor_col, retry_transition.document_append.start_col, ); try materialized_append.appendSlice( alloc, retry_transition.document_append.bytes, ); try expectDocumentWireEqualsLogical( alloc, grown_flow.items[stable_flow.len..], materialized_append.items, );
var retry_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = &retry_prepared, }; const retry_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = retry_plan, .scroll_plan = retry_scroll_plan, .document_append = retry_transition.document_append, .body_painter = .{ .ctx = &retry_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, retry_result.shadow_state, ); row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(1, &row_text); try std.testing.expectEqualStrings("tail 0", row_text.items); row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(4, &row_text); try std.testing.expectEqualStrings("tail 3", row_text.items);
runtime.consumeTranscriptTransition(alloc, &retry_transition, retry_result); const stable = runtime.stableTranscriptProjectionForFlow(grown_flow.items) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(retry_facts.target_visual_offset, stable.visual_offset); try std.testing.expectEqual(retry_transition.cursor_row, stable.cursor_row); try std.testing.expectEqual(retry_transition.cursor_col, stable.cursor_col);}
test "folded inexact growth advances only materialized viewport rows" { const alloc = std.testing.allocator; const cols: u16 = 16; const viewport_rows: u16 = 4; const growth_rows: usize = @as(usize, viewport_rows) * 2; const growth_rows_u32: u32 = @intCast(growth_rows); const initial_owned_top = viewport_rows + 1; const content_bottom = initial_owned_top + viewport_rows - 1; const stable_flow = "summary"; var seed_transition = try createProductionCappedFoldedRecoveryTransition( alloc, cols, viewport_rows, ); defer seed_transition.deinit(alloc); try std.testing.expectEqual(@as(?usize, null), seed_transition.materialized.flow_len); try std.testing.expect( seed_transition.semantic_rows > seed_transition.scroll_plan.requested_inline_advance_rows, );
var grown_flow: std.ArrayList(u8) = .empty; defer grown_flow.deinit(alloc); try grown_flow.ensureTotalCapacity( alloc, stable_flow.len + growth_rows * "\ng00000".len, ); grown_flow.appendSliceAssumeCapacity(stable_flow); var growth_index: usize = 0; while (growth_index < growth_rows) : (growth_index += 1) { var line_buf: [16]u8 = undefined; const line = try std.fmt.bufPrint( &line_buf, "\ng{d:0>5}", .{growth_index}, ); grown_flow.appendSliceAssumeCapacity(line); }
var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile( std.testing.io, "folded-staged-projection.log", .{ .read = true }, ); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = transcriptTestLayout( cols, content_bottom + 4, content_bottom, ), .owned_top_row = initial_owned_top, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.shadow_vt.?.feed("shell0\r\nshell1\r\nshell2\r\nshell3"); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 1, }); var fold_index: usize = 0; while (fold_index < viewport_rows) : (fold_index += 1) { var fold_buf: [16]u8 = undefined; const fold = try std.fmt.bufPrint(&fold_buf, "fold{d}", .{fold_index}); try appendFoldedLineForTest(&runtime, alloc, 0, fold); }
var metrics: Metrics = .{}; var stable_source = try testFoldedSource( alloc, stable_flow, runtime.layout.cols, 1, ); defer stable_source.deinit(alloc); var stable_fixed_prepared: ?transcript_painter.PreparedTranscriptSurfacePaint = null; defer if (stable_fixed_prepared) |*prepared| prepared.deinit(alloc); const stable_frame = try solveTestTranscriptFixedPointFrame( &runtime, alloc, &metrics, &stable_source, 4, &stable_fixed_prepared, ); const stable_fixed = &stable_fixed_prepared.?; try std.testing.expectEqual(@as(u32, 0), preparedVisualOffsetForTest(stable_fixed)); try std.testing.expectEqual( @as(u32, viewport_rows), totalVisualRowsForTest(stable_fixed.sourceVisualRows()), ); try std.testing.expectEqual( stable_frame.layout.owned_top, stable_frame.scroll_plan.post_scroll_owned_top, ); var stable_plan = testPaintPlanForFrame( stable_frame.layout, stable_fixed.selection, stable_fixed.cursor.cursor_row, stable_fixed.cursor.cursor_col, ); var stable_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &stable_source, stable_fixed, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(stable_frame.layout), &stable_plan, stable_frame.scroll_plan, ); defer stable_transition.deinit(alloc); var stable_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = stable_fixed, }; const stable_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = stable_plan, .scroll_plan = stable_frame.scroll_plan, .body_painter = .{ .ctx = &stable_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); runtime.ackPreservedRowReleaseAssumeValid( stable_frame.scroll_plan, stable_result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &stable_transition, stable_result); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); try runtime.shadow_vt.?.rowTextTrimmed(initial_owned_top, &row_text); try std.testing.expectEqualStrings("│ fold0", row_text.items); row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(content_bottom, &row_text); try std.testing.expectEqualStrings("│ fold3", row_text.items);
var history: std.ArrayList(u8) = .empty; defer history.deinit(alloc); var seed_source = try testFoldedSource( alloc, grown_flow.items, runtime.layout.cols, 1, ); defer seed_source.deinit(alloc); seed_source.preview.natural_visual_rows = @intCast(@as(u32, viewport_rows) + growth_rows_u32); const seed_release = render_engine.frame_layout.solveWithPreservedRowRelease(.{ .terminal = runtime.layout, .owned_top = runtime.owned_top_row, .footer = .{ .natural_rows = 8, .min_rows = 8, .max_rows = 8, }, .transcript = seed_source.preview, .prior = runtime.committed_frame_layout, }, 0); const compact_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, seed_release.released_rows, seed_transition.scroll_plan.requested_inline_advance_rows, ); try std.testing.expectEqualDeep(seed_transition.scroll_plan, compact_scroll_plan); try std.testing.expectEqual( seed_release.layout.owned_top, seed_transition.scroll_plan.post_scroll_owned_top, ); const seed_target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(seed_release.layout); try std.testing.expectEqualDeep(seed_transition.target_layout, seed_target_layout);
var seed_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &seed_source, ); defer seed_prepared.deinit(alloc); const seed_target_offset = preparedVisualOffsetForTest(&seed_prepared); try std.testing.expectEqual(growth_rows_u32, seed_target_offset); const seed_total_visual_rows = totalVisualRowsForTest(seed_prepared.sourceVisualRows()); try std.testing.expectEqual( @as(u32, viewport_rows) + growth_rows_u32, seed_total_visual_rows, ); const seed_projection = try transcript_painter.stagePreparedTranscriptForVisualOffset( alloc, runtime.layout, &seed_prepared, seed_release.layout.transcript_area, seed_transition.visual_offset, ); try std.testing.expectEqual( seed_transition.materialized.flow_len, seed_projection.flow_end, ); try std.testing.expectEqual( seed_transition.materialized.visual_rows, seed_prepared.projection_visual_rows.?, ); try std.testing.expectEqual( seed_transition.materialized.visual_offset, seed_transition.visual_offset, );
var contract_selection = seed_prepared.selection; contract_selection.line_count = seed_transition.selection.line_count; contract_selection.tail_kind = seed_transition.selection.tail_kind; try std.testing.expectEqualDeep(seed_transition.selection, contract_selection); try std.testing.expectEqual( seed_transition.materialized.cursor_row, seed_prepared.cursor.cursor_row, ); try std.testing.expectEqual( seed_transition.materialized.cursor_col, seed_prepared.cursor.cursor_col, ); try std.testing.expectEqual( seed_transition.cursor_row, seed_prepared.cursor.cursor_row, ); try std.testing.expectEqual( seed_transition.cursor_col, seed_prepared.cursor.cursor_col, ); try std.testing.expectEqual( seed_transition.replaceable_last_line, seed_prepared.replaceable_last_line, ); try std.testing.expectEqual( seed_transition.replaceable_start, seed_prepared.replaceable_start, ); try std.testing.expectEqual( seed_transition.replaceable_row, seed_prepared.cursor.replaceable_row, ); try std.testing.expectEqualSlices( usize, seed_transition.folded_summary_indices, seed_source.folded_summary_indices, ); try std.testing.expect(seed_transition.document_append.isEmpty()); try std.testing.expectEqual(@as(usize, 0), seed_transition.document_append_bytes.len);
const compact_target_flow = try alloc.dupe(u8, grown_flow.items); if (seed_transition.target_flow.len > 0) alloc.free(seed_transition.target_flow); seed_transition.target_flow = compact_target_flow; seed_transition.target_cache.clearRetainingCapacity(); try seed_transition.target_cache.appendSlice(alloc, grown_flow.items); seed_transition.semantic_rows = growth_rows_u32; seed_transition.semantic_progress_rows = growth_rows_u32; seed_transition.selection = seed_prepared.selection; seed_transition.source_endpoint_visual_offset = seed_target_offset; seed_transition.total_visual_rows = seed_total_visual_rows;
try appendGridRows( alloc, runtime.shadow_vt.?, 1, seed_transition.scroll_plan.terminal_scroll_rows, &history, &row_text, ); const seed_plan = testPaintPlanForFrame( seed_release.layout, seed_prepared.selection, seed_prepared.cursor.cursor_row, seed_prepared.cursor.cursor_col, ); var seed_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = &seed_prepared, }; const seed_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = seed_plan, .scroll_plan = seed_transition.scroll_plan, .document_append = seed_transition.document_append, .body_painter = .{ .ctx = &seed_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, seed_result.shadow_state, ); try std.testing.expectEqual( seed_transition.scroll_plan.terminal_scroll_rows, seed_result.terminal_scroll_rows_committed, ); try std.testing.expect(!seed_result.document_append_committed); runtime.ackPreservedRowReleaseAssumeValid( seed_transition.scroll_plan, seed_result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &seed_transition, seed_result); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expectEqual( seed_transition.visual_offset, runtime.transcriptCommitDiagnostic().visual_offset, ); try std.testing.expectEqual( growth_rows_u32 - seed_transition.scroll_plan.requested_inline_advance_rows, runtime.transcriptCommitDiagnostic().remaining_inline_rows, );
var attempt_count: usize = 0; while (runtime.transcriptCommitDiagnostic().state == .recovering) : (attempt_count += 1) { var source = try testFoldedSource( alloc, grown_flow.items, runtime.layout.cols, 1, ); defer source.deinit(alloc); source.preview.natural_visual_rows = @intCast(@as(u32, viewport_rows) + growth_rows_u32); var prepared: ?transcript_painter.PreparedTranscriptSurfacePaint = null; defer if (prepared) |*paint| paint.deinit(alloc); const frame = try solveTestTranscriptFixedPointFrame( &runtime, alloc, &metrics, &source, 8, &prepared, ); const paint = &prepared.?; try std.testing.expectEqual( frame.layout.owned_top, frame.scroll_plan.post_scroll_owned_top, ); try std.testing.expectEqual( viewport_rows, frame.scroll_plan.acceptedInlineRows( frame.scroll_plan.terminal_scroll_rows, ), ); try appendGridRows( alloc, runtime.shadow_vt.?, 1, frame.scroll_plan.terminal_scroll_rows, &history, &row_text, );
var plan = testPaintPlanForFrame( frame.layout, paint.selection, paint.cursor.cursor_row, paint.cursor.cursor_col, ); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, paint, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(frame.layout), &plan, frame.scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty()); try std.testing.expectEqual( @as(?usize, null), transition.materialized.flow_len, ); try std.testing.expectEqual( viewport_rows, transition.materialized.visual_rows, );
var painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = paint, }; const result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = plan, .scroll_plan = frame.scroll_plan, .document_append = transition.document_append, .body_painter = .{ .ctx = &painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, result.shadow_state, ); try std.testing.expectEqual( frame.scroll_plan.terminal_scroll_rows, result.terminal_scroll_rows_committed, ); runtime.ackPreservedRowReleaseAssumeValid( frame.scroll_plan, result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &transition, result); } try std.testing.expectEqual(@as(usize, 1), attempt_count);
const stable = runtime.stableTranscriptProjectionForFlow(grown_flow.items) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(growth_rows_u32, stable.visual_offset); try std.testing.expectEqual(@as(u16, 4), stable.cursor_row); try std.testing.expectEqual(@as(u16, 7), stable.cursor_col); try std.testing.expectEqual(runtime.shadow_vt.?.cursor_row, stable.cursor_row); try std.testing.expectEqual(runtime.shadow_vt.?.cursor_col, stable.cursor_col);
const followup_rows: usize = 5; const followup_append = "\ng00008\ng00009\ng00010\ng00011\ng00012"; var followup_flow: std.ArrayList(u8) = .empty; defer followup_flow.deinit(alloc); try followup_flow.appendSlice(alloc, grown_flow.items); try followup_flow.appendSlice(alloc, followup_append); var followup_source = try testFoldedSource( alloc, followup_flow.items, runtime.layout.cols, 1, ); defer followup_source.deinit(alloc); followup_source.preview.natural_visual_rows = @intCast(@as(usize, viewport_rows) + growth_rows + followup_rows); var followup_prepared: ?transcript_painter.PreparedTranscriptSurfacePaint = null; defer if (followup_prepared) |*paint| paint.deinit(alloc); const followup_frame = try solveTestTranscriptFixedPointFrame( &runtime, alloc, &metrics, &followup_source, 4, &followup_prepared, ); const followup_paint = &followup_prepared.?; try std.testing.expectEqual( followup_frame.layout.owned_top, followup_frame.scroll_plan.post_scroll_owned_top, ); const followup_facts = runtime.planTranscriptScroll(followup_paint); try std.testing.expectEqual(@as(u32, 1), followup_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), followup_facts.planned_rows); try appendGridRows( alloc, runtime.shadow_vt.?, 1, followup_frame.scroll_plan.terminal_scroll_rows, &history, &row_text, ); var followup_plan = testPaintPlanForFrame( followup_frame.layout, followup_paint.selection, followup_paint.cursor.cursor_row, followup_paint.cursor.cursor_col, ); var followup_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &followup_source, followup_paint, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(followup_frame.layout), &followup_plan, followup_frame.scroll_plan, ); defer followup_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, followup_append, followup_transition.document_append.bytes, ); try std.testing.expectEqual( followup_flow.items.len, followup_transition.materialized.flow_len.?, ); try std.testing.expectEqual(@as(u16, 4), followup_transition.document_append.start_row); try std.testing.expectEqual(@as(u16, 7), followup_transition.document_append.start_col);
var followup_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = followup_paint, }; const followup_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = followup_plan, .scroll_plan = followup_frame.scroll_plan, .document_append = followup_transition.document_append, .body_painter = .{ .ctx = &followup_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, followup_result.shadow_state, ); try std.testing.expect(followup_result.document_append_committed); try std.testing.expectEqual( @as(u16, 1), followup_result.terminal_scroll_rows_committed, );
runtime.ackPreservedRowReleaseAssumeValid( followup_frame.scroll_plan, followup_result.terminal_scroll_rows_committed, ); runtime.consumeTranscriptTransition(alloc, &followup_transition, followup_result); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
try appendGridRows( alloc, runtime.shadow_vt.?, followup_frame.layout.transcript_area.top, followup_frame.layout.transcript_area.height(), &history, &row_text, ); const expected_history = "shell0\n" ++ "shell1\n" ++ "shell2\n" ++ "shell3\n" ++ "│ fold0\n" ++ "│ fold1\n" ++ "│ fold2\n" ++ "│ fold3\n" ++ "g00000\n" ++ "g00001\n" ++ "g00002\n" ++ "g00003\n" ++ "g00004\n" ++ "g00005\n" ++ "g00006\n" ++ "g00007\n" ++ "g00008\n" ++ "g00009\n" ++ "g00010\n" ++ "g00011\n" ++ "g00012"; try std.testing.expectEqualStrings(expected_history, history.items); try std.testing.expect(std.mem.indexOf(u8, history.items, "\n\n") == null);
fold_index = 0; while (fold_index < viewport_rows) : (fold_index += 1) { var expected_buf: [16]u8 = undefined; const expected = try std.fmt.bufPrint( &expected_buf, "│ fold{d}", .{fold_index}, ); try std.testing.expectEqual( @as(usize, 1), countExactHistoryLine(history.items, expected), ); } growth_index = 0; while (growth_index < growth_rows + followup_rows) : (growth_index += 1) { var expected_buf: [16]u8 = undefined; const expected = try std.fmt.bufPrint( &expected_buf, "g{d:0>5}", .{growth_index}, ); try std.testing.expectEqual( @as(usize, 1), countExactHistoryLine(history.items, expected), ); }}
test "staged soft-wrapped presentation resumes across committed projections" { const alloc = std.testing.allocator; const cols: u16 = 8; const stable_flow = "base\n"; const filler_row_count: usize = @as(usize, std.math.maxInt(u16)) - 3; const link_url = "https://staged.example"; const link_params = "id=fx-42"; const presentation_open = "\x1b[1;31m\x1b[9m" ++ "\x1b]8;" ++ link_params ++ ";" ++ link_url ++ "\x1b\\"; const presentation_close = "\x1b]8;;\x1b\\\x1b[29m\x1b[0m"; const resume_bytes = "\x1b[0m\x1b[1m\x1b[9m\x1b[31m" ++ "\x1b]8;" ++ link_params ++ ";" ++ link_url ++ "\x1b\\"; const steady_bytes = "\x1b]8;;\x1b\\\x1b[0m\r"; const linked_row_symbols = "0123456789A";
var linked_text = try alloc.alloc(u8, linked_row_symbols.len * cols); defer alloc.free(linked_text); for (linked_row_symbols, 0..) |symbol, row| { @memset(linked_text[row * cols .. (row + 1) * cols], symbol); }
var grown_flow: std.ArrayList(u8) = .empty; defer grown_flow.deinit(alloc); try grown_flow.ensureTotalCapacity( alloc, stable_flow.len + filler_row_count * "x\n".len + presentation_open.len + linked_text.len + presentation_close.len, ); grown_flow.appendSliceAssumeCapacity(stable_flow); for (0..filler_row_count) |_| { grown_flow.appendSliceAssumeCapacity("x\n"); } grown_flow.appendSliceAssumeCapacity(presentation_open); grown_flow.appendSliceAssumeCapacity(linked_text); grown_flow.appendSliceAssumeCapacity(presentation_close);
var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile( std.testing.io, "staged-presentation.log", .{ .read = true }, ); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = transcriptTestLayout(cols, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.shadow_vt.?.feed(stable_flow);
var stable_source = try testSource(alloc, stable_flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, stable_flow, stable_prepared.selection, 0, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 414, );
var raw_append_bytes: std.ArrayList(u8) = .empty; defer raw_append_bytes.deinit(alloc);
var first_source = try testSource(alloc, grown_flow.items, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expectEqual( @as(u16, std.math.maxInt(u16)), first_facts.planned_rows, ); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc);
const first_raw_end = first_transition.materialized.flow_len orelse return error.TestUnexpectedResult; const first_raw_append = grown_flow.items[stable_flow.len..first_raw_end]; try std.testing.expect(first_raw_end < grown_flow.items.len); try std.testing.expect(std.mem.endsWith( u8, first_transition.document_append.bytes, steady_bytes, )); try expectDocumentWireEqualsLogical( alloc, first_raw_append, first_transition.document_append.bytes[0 .. first_transition.document_append.bytes.len - steady_bytes.len], ); try raw_append_bytes.appendSlice(alloc, first_raw_append);
var first_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = &first_prepared, }; var metrics: Metrics = .{}; const first_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = first_plan, .scroll_plan = first_scroll_plan, .document_append = first_transition.document_append, .body_painter = .{ .ctx = &first_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, first_result.shadow_state, ); try std.testing.expect(runtime.shadow_vt.?.current_style.eql(.{})); try std.testing.expect(!runtime.shadow_vt.?.pending_wrap);
var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); for ("2345", 1..) |symbol, row| { row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(@intCast(row), &row_text); var expected_row: [cols]u8 = undefined; @memset(&expected_row, symbol); try std.testing.expectEqualStrings(&expected_row, row_text.items); const cell = runtime.shadow_vt.?.cellAt(@intCast(row), 1).?; try std.testing.expect(cell.style.flags.bold); try std.testing.expect(cell.style.flags.strike); try std.testing.expect(cell.style.fg.eql(.{ .indexed = 1 })); try std.testing.expect(cell.style.hyperlink_id != 0); try std.testing.expectEqualStrings( link_url, runtime.shadow_vt.?.hyperlinkUrl(cell.style.hyperlink_id).?, ); }
runtime.consumeTranscriptTransition(alloc, &first_transition, first_result); const after_first = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, after_first.state, ); try std.testing.expect(after_first.remaining_inline_rows > 0);
var retry_source = try testSource(alloc, grown_flow.items, runtime.layout.cols); defer retry_source.deinit(alloc); var retry_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &retry_source, ); defer retry_prepared.deinit(alloc); const retry_facts = runtime.planTranscriptScroll(&retry_prepared); const retry_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, retry_facts.planned_rows, ); var retry_plan = testPaintPlan(&runtime, retry_prepared.selection); var retry_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &retry_source, &retry_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(retry_plan), &retry_plan, retry_scroll_plan, ); defer retry_transition.deinit(alloc);
const retry_raw_append = grown_flow.items[first_raw_end..]; try std.testing.expect(std.mem.startsWith( u8, retry_transition.document_append.bytes, resume_bytes, )); try expectDocumentWireEqualsLogical( alloc, retry_raw_append, retry_transition.document_append.bytes[resume_bytes.len..], ); try raw_append_bytes.appendSlice(alloc, retry_raw_append); try std.testing.expectEqualStrings( grown_flow.items[stable_flow.len..], raw_append_bytes.items, );
var retry_painter = PreparedTranscriptFramePainter{ .runtime = &runtime, .alloc = alloc, .prepared = &retry_prepared, }; const retry_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = retry_plan, .scroll_plan = retry_scroll_plan, .document_append = retry_transition.document_append, .body_painter = .{ .ctx = &retry_painter, .paint = PreparedTranscriptFramePainter.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, retry_result.shadow_state, ); try std.testing.expect(runtime.shadow_vt.?.current_style.eql(.{})); try std.testing.expect(!runtime.shadow_vt.?.pending_wrap);
for ("789A", 1..) |symbol, row| { row_text.clearRetainingCapacity(); try runtime.shadow_vt.?.rowTextTrimmed(@intCast(row), &row_text); var expected_row: [cols]u8 = undefined; @memset(&expected_row, symbol); try std.testing.expectEqualStrings(&expected_row, row_text.items); const cell = runtime.shadow_vt.?.cellAt(@intCast(row), 1).?; try std.testing.expect(cell.style.flags.bold); try std.testing.expect(cell.style.flags.strike); try std.testing.expect(cell.style.fg.eql(.{ .indexed = 1 })); try std.testing.expect(cell.style.hyperlink_id != 0); try std.testing.expectEqualStrings( link_url, runtime.shadow_vt.?.hyperlinkUrl(cell.style.hyperlink_id).?, ); }
runtime.consumeTranscriptTransition(alloc, &retry_transition, retry_result); const stable = runtime.stableTranscriptProjectionForFlow(grown_flow.items) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(retry_facts.target_visual_offset, stable.visual_offset); try std.testing.expectEqual( @as(?usize, grown_flow.items.len), retry_transition.materialized.flow_len, );}
test "uncommitted stable-origin append retries from the materialized source endpoint" { const alloc = std.testing.allocator; const stable_flow = "base\n"; const grown_flow = stable_flow ++ "0\n1\n2\n3\n4"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var stable_source = try testSource(alloc, stable_flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, stable_flow, stable_prepared.selection, 0, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 413, );
var first_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); const first_facts = runtime.planTranscriptScroll(&first_prepared); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[stable_flow.len..], first_transition.document_append.bytes, );
runtime.consumeTranscriptTransition(alloc, &first_transition, .{ .bytes_written = 1, .changed_cells = 0, .full_repaint = false, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = stable_prepared.cursor.cursor_row, .committed_cursor_col = stable_prepared.cursor.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
var retry_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer retry_source.deinit(alloc); var retry_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &retry_source, ); defer retry_prepared.deinit(alloc); const retry_facts = runtime.planTranscriptScroll(&retry_prepared); const retry_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, retry_facts.planned_rows, ); var retry_plan = testPaintPlan(&runtime, retry_prepared.selection); var retry_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &retry_source, &retry_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(retry_plan), &retry_plan, retry_scroll_plan, ); defer retry_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[stable_flow.len..], retry_transition.document_append.bytes, );}
test "staged wrapped projection clips paint and flow at one renderer boundary" { const alloc = std.testing.allocator; const flow = "base\nabcdefghijklmnopqrst"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(5, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc); const projection = try transcript_painter.stagePreparedTranscriptForVisualOffset( alloc, runtime.layout, &prepared, .{ .top = 1, .bottom = 2 }, 2, ); try std.testing.expectEqual(@as(?usize, "base\n".len + 15), projection.flow_end); try std.testing.expectEqual(@as(usize, 1), prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 1), prepared.selection.partial_skip_rows); try std.testing.expectEqual(@as(u16, 3), prepared.cursor.cursor_row); try std.testing.expectEqual(@as(u16, 1), prepared.cursor.cursor_col);
var shadow = try vt_emulator.Grid.init( alloc, runtime.layout.cols, runtime.layout.rows, ); defer shadow.deinit(); var plan = testPaintPlan(&runtime, prepared.selection); plan.viewport = prepared.selection; var surface = try render_engine.frame_surface.FrameSurface.initFromShadow( alloc, plan, shadow, ); defer surface.deinit(); _ = try runtime.paintPreparedTranscriptIntoSurface( alloc, &surface, &prepared, ); var target = try surface.copyToTargetGrid(alloc); defer target.deinit(); var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); try target.rowTextTrimmed(1, &row_text); try std.testing.expectEqualStrings("fghij", row_text.items); row_text.clearRetainingCapacity(); try target.rowTextTrimmed(2, &row_text); try std.testing.expectEqualStrings("klmno", row_text.items); try std.testing.expectEqual(@as(u16, 3), target.cursor_row); try std.testing.expectEqual(@as(u16, 1), target.cursor_col);}
test "width change does not compare semantic offsets across projections" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest(&runtime, alloc, "stable", testSelection(4), 4, 3, 1, 42); runtime.layout = transcriptTestLayout(10, 8, 4);
var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = try alloc.dupe(u8, "stable"), .selection = testSelection(9), }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &([_]u16{1} ** 16));
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u16, 0), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), facts.planned_rows);}
test "stable origin projection rebase narrow to wide survives repeated partial and restores append" { const alloc = std.testing.allocator; const flow = "abcdefghijklmnopqrstuvwxyz0123456789\n" ++ "one\n" ++ "two\n" ++ "three\n" ++ "four\n" ++ "five\n"; const grown_flow = flow ++ "six\nseven\neight\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(5, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var stable_source = try testSource(alloc, flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); const narrow_offset = preparedVisualOffsetForTest(&stable_prepared); try std.testing.expect(narrow_offset > 0); try installStableAnchorForTest( &runtime, alloc, flow, stable_prepared.selection, narrow_offset, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 420, );
runtime.layout = transcriptTestLayout(20, 8, 4); var first_source = try testSource(alloc, flow, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); const wide_offset = preparedVisualOffsetForTest(&first_prepared); try std.testing.expect(wide_offset < narrow_offset);
const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expect(first_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), first_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), first_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), first_facts.planned_rows); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc); try std.testing.expect(first_transition.recovery_rebase); try std.testing.expect(first_transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &first_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = first_transition.cursor_row, .committed_cursor_col = first_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); var recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(wide_offset, recovery.visual_offset); try std.testing.expectEqual(@as(u16, 0), recovery.remaining_inline_rows); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null);
var repeated_source = try testSource(alloc, flow, runtime.layout.cols); defer repeated_source.deinit(alloc); var repeated_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &repeated_source, ); defer repeated_prepared.deinit(alloc); const repeated_facts = runtime.planTranscriptScroll(&repeated_prepared); try std.testing.expect(repeated_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), repeated_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.planned_rows); const repeated_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, repeated_facts.planned_rows, ); var repeated_plan = testPaintPlan(&runtime, repeated_prepared.selection); var repeated_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &repeated_source, &repeated_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(repeated_plan), &repeated_plan, repeated_scroll_plan, ); defer repeated_transition.deinit(alloc); try std.testing.expect(repeated_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &repeated_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = repeated_transition.cursor_row, .committed_cursor_col = repeated_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(wide_offset, recovery.visual_offset);
var complete_source = try testSource(alloc, flow, runtime.layout.cols); defer complete_source.deinit(alloc); var complete_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &complete_source, ); defer complete_prepared.deinit(alloc); const complete_facts = runtime.planTranscriptScroll(&complete_prepared); try std.testing.expect(complete_facts.recovery_rebase); const complete_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, complete_facts.planned_rows, ); var complete_plan = testPaintPlan(&runtime, complete_prepared.selection); var complete_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &complete_source, &complete_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(complete_plan), &complete_plan, complete_scroll_plan, ); defer complete_transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &complete_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = complete_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = complete_transition.cursor_row, .committed_cursor_col = complete_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const stable = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(wide_offset, stable.visual_offset);
var append_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer append_source.deinit(alloc); var append_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &append_source, ); defer append_prepared.deinit(alloc); const append_facts = runtime.planTranscriptScroll(&append_prepared); try std.testing.expect(!append_facts.recovery_rebase); try std.testing.expect(append_facts.semantic_rows > 0); const append_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, append_facts.planned_rows, ); var append_plan = testPaintPlan(&runtime, append_prepared.selection); var append_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &append_source, &append_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(append_plan), &append_plan, append_scroll_plan, ); defer append_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[flow.len..], append_transition.document_append.bytes, );}
test "stable origin projection rebase wide to narrow retains debt until exhausted" { const alloc = std.testing.allocator; const flow = "stable\nbase\n"; const grown_flow = flow ++ "next\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try enterResizeReflowRecovery( &runtime, alloc, null, .terminal_partial_write, );
var repeated_source = try testSource(alloc, flow, runtime.layout.cols); defer repeated_source.deinit(alloc); var repeated_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &repeated_source, ); defer repeated_prepared.deinit(alloc); const repeated_facts = runtime.planTranscriptScroll(&repeated_prepared); try std.testing.expect(repeated_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 1), repeated_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), repeated_facts.planned_rows); const repeated_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, repeated_facts.planned_rows, ); var repeated_plan = testPaintPlan(&runtime, repeated_prepared.selection); var repeated_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &repeated_source, &repeated_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(repeated_plan), &repeated_plan, repeated_scroll_plan, ); defer repeated_transition.deinit(alloc); try std.testing.expect(repeated_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &repeated_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = repeated_transition.cursor_row, .committed_cursor_col = repeated_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); var recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null);
var complete_source = try testSource(alloc, flow, runtime.layout.cols); defer complete_source.deinit(alloc); var complete_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &complete_source, ); defer complete_prepared.deinit(alloc); const complete_facts = runtime.planTranscriptScroll(&complete_prepared); try std.testing.expect(complete_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 1), complete_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), complete_facts.planned_rows); const complete_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, complete_facts.planned_rows, ); var complete_plan = testPaintPlan(&runtime, complete_prepared.selection); var complete_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &complete_source, &complete_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(complete_plan), &complete_plan, complete_scroll_plan, ); defer complete_transition.deinit(alloc); try std.testing.expect(complete_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &complete_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = complete_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = complete_transition.cursor_row, .committed_cursor_col = complete_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, recovery.state, ); try std.testing.expectEqual(@as(u16, 0), recovery.remaining_inline_rows);
var append_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer append_source.deinit(alloc); var append_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &append_source, ); defer append_prepared.deinit(alloc); const append_facts = runtime.planTranscriptScroll(&append_prepared); try std.testing.expect(!append_facts.recovery_rebase); try std.testing.expect(append_facts.semantic_rows > 0); const append_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, append_facts.planned_rows, ); var append_plan = testPaintPlan(&runtime, append_prepared.selection); var append_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &append_source, &append_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(append_plan), &append_plan, append_scroll_plan, ); defer append_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[flow.len..], append_transition.document_append.bytes, );}
test "stable origin projection rebase measured history is not semantic debt" { const alloc = std.testing.allocator; const flow = "0\n1\n2\n3\n4\n5\n6\n7"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var stable_source = try testSource(alloc, flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); const stable_offset = preparedVisualOffsetForTest(&stable_prepared); try installStableAnchorForTest( &runtime, alloc, flow, stable_prepared.selection, stable_offset, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 421, );
runtime.layout = transcriptTestLayout(20, 8, 4); runtime.resize_history_row_delta = 2; var first_source = try testSource(alloc, flow, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); const measured_offset = preparedVisualOffsetForTest(&first_prepared); try std.testing.expect(measured_offset > stable_offset);
const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expect(first_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), first_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), first_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), first_facts.planned_rows); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &first_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = first_transition.cursor_row, .committed_cursor_col = first_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null);
var repeated_source = try testSource(alloc, flow, runtime.layout.cols); defer repeated_source.deinit(alloc); var repeated_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &repeated_source, ); defer repeated_prepared.deinit(alloc); const repeated_facts = runtime.planTranscriptScroll(&repeated_prepared); try std.testing.expect(repeated_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), repeated_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.planned_rows); const repeated_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, repeated_facts.planned_rows, ); var repeated_plan = testPaintPlan(&runtime, repeated_prepared.selection); var repeated_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &repeated_source, &repeated_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(repeated_plan), &repeated_plan, repeated_scroll_plan, ); defer repeated_transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &repeated_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = repeated_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = repeated_transition.cursor_row, .committed_cursor_col = repeated_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); runtime.resize_history_row_delta = null;
const projection = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(measured_offset, projection.visual_offset);}
test "overlay frame retains an exact-end measured history floor after reset" { const alloc = std.testing.allocator; const flow = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 40, 36), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc);
var history_offset: u32 = 0; for (prepared.sourceVisualRows()) |rows| history_offset += rows; var history_selection = prepared.selection; history_selection.start_line = prepared.sourceVisualRows().len; history_selection.partial_skip_rows = 0; history_selection.last_visible_row = 1;
var plan = testPaintPlan(&runtime, prepared.selection); plan.activity = .{ .overlay_entry = 1 }; const committed_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); runtime.committed_frame_layout = committed_layout; try installStableAnchorForTest( &runtime, alloc, flow, history_selection, history_offset, 1, 1, committed_layout.layout_id, );
const facts = runtime.planTranscriptScroll(&prepared); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, committed_layout, &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqual(history_offset, transition.visual_offset); try std.testing.expectEqual( prepared.sourceVisualRows().len, transition.selection.start_line, );}
test "zero measured reverse restores exact source origin" { const alloc = std.testing.allocator; const flow = "line 00 abcdefghij\n" ++ "line 01 abcdefghij\n" ++ "line 02 abcdefghij\n" ++ "line 03 abcdefghij\n" ++ "line 04 abcdefghij\n" ++ "line 05 abcdefghij\n" ++ "line 06 abcdefghij\n" ++ "line 07 abcdefghij\n" ++ "line 08 abcdefghij\n" ++ "line 09 abcdefghij\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var narrow_source = try testSource(alloc, flow, 12); defer narrow_source.deinit(alloc);
try expectZeroReverseRestoresSourceOrigin( &runtime, alloc, &wide_source, &narrow_source, 2, .seeded_exact, );}
test "zero measured reverse restores folded source origin without endpoint" { const alloc = std.testing.allocator; const flow = "head abcdefghij\n" ++ "summary\n" ++ "tail 00 abcdefghij\n" ++ "tail 01 abcdefghij\n" ++ "tail 02 abcdefghij\n" ++ "tail 03 abcdefghij\n" ++ "tail 04 abcdefghij\n" ++ "tail 05 abcdefghij\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2, }); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output alpha"); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output beta");
var wide_source = try testFoldedSource(alloc, flow, runtime.layout.cols, 2); defer wide_source.deinit(alloc); var narrow_source = try testFoldedSource(alloc, flow, 12, 2); defer narrow_source.deinit(alloc);
try expectZeroReverseRestoresSourceOrigin( &runtime, alloc, &wide_source, &narrow_source, 2, .natural_null, );}
test "zero measured reverse restores folded source origin with inexact endpoint mapping" { const alloc = std.testing.allocator; const flow = "head abcdefghij\n" ++ "summary\n" ++ "tail 00 abcdefghij\n" ++ "tail 01 abcdefghij\n" ++ "tail 02 abcdefghij\n" ++ "tail 03 abcdefghij\n" ++ "tail 04 abcdefghij\n" ++ "tail 05 abcdefghij\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2, }); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output alpha"); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output beta");
var wide_source = try testFoldedSource(alloc, flow, runtime.layout.cols, 2); defer wide_source.deinit(alloc); var narrow_source = try testFoldedSource(alloc, flow, 12, 2); defer narrow_source.deinit(alloc);
try expectZeroReverseRestoresSourceOrigin( &runtime, alloc, &wide_source, &narrow_source, 2, .seeded_inexact, );}
test "measured history origin matrix restores hard soft wide and folded sources" { const alloc = std.testing.allocator; const Case = struct { source_cols: u16, target_cols: u16, positive_delta: i32, flow: []const u8, folded: bool = false, endpoint_mode: MeasuredEndpointMode, }; const cases = [_]Case{ .{ .source_cols = 24, .target_cols = 12, .positive_delta = 2, .flow = "hard 00\nhard 01\nhard 02\nhard 03\nhard 04\nhard 05\n" ++ "hard 06\nhard 07\nhard 08\nhard 09\nhard 10\nhard 11\n", .endpoint_mode = .seeded_exact, }, .{ .source_cols = 20, .target_cols = 8, .positive_delta = 3, .flow = "soft-wrap-alpha-000\nsoft-wrap-beta-001\nsoft-wrap-gamma-002\n" ++ "soft-wrap-delta-003\nsoft-wrap-epsilon-004\nsoft-wrap-zeta-005\n" ++ "soft-wrap-eta-006\nsoft-wrap-theta-007\nsoft-wrap-iota-008\n" ++ "soft-wrap-kappa-009\n", .endpoint_mode = .seeded_exact, }, .{ .source_cols = 16, .target_cols = 8, .positive_delta = 2, .flow = "wide 界界 00\nwide 界界 01\nwide 界界 02\nwide 界界 03\n" ++ "wide 界界 04\nwide 界界 05\nwide 界界 06\nwide 界界 07\n" ++ "wide 界界 08\nwide 界界 09\nwide 界界 10\nwide 界界 11\n", .endpoint_mode = .seeded_exact, }, .{ .source_cols = 24, .target_cols = 12, .positive_delta = 2, .flow = "head abcdefghij\nsummary\ntail 00 abcdefghij\n" ++ "tail 01 abcdefghij\ntail 02 abcdefghij\ntail 03 abcdefghij\n" ++ "tail 04 abcdefghij\ntail 05 abcdefghij\n", .folded = true, .endpoint_mode = .natural_null, }, .{ .source_cols = 30, .target_cols = 10, .positive_delta = 3, .flow = "head abcdefghij\nsummary\ntail 00 abcdefghij\n" ++ "tail 01 abcdefghij\ntail 02 abcdefghij\ntail 03 abcdefghij\n" ++ "tail 04 abcdefghij\ntail 05 abcdefghij\n", .folded = true, .endpoint_mode = .seeded_inexact, }, };
for (cases) |case| { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(case.source_cols, 12, 8), .owned_top_row = 1, .full_transcript = .{ .depth = if (case.folded) .full else .inline_mode }, }; defer runtime.deinit(alloc); if (case.folded) { try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2, }); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output alpha"); try appendFoldedLineForTest(&runtime, alloc, 0, "folded output beta"); }
var wide_source = if (case.folded) try testFoldedSource(alloc, case.flow, case.source_cols, 2) else try testSource(alloc, case.flow, case.source_cols); defer wide_source.deinit(alloc); var narrow_source = if (case.folded) try testFoldedSource(alloc, case.flow, case.target_cols, 2) else try testSource(alloc, case.flow, case.target_cols); defer narrow_source.deinit(alloc);
try expectZeroReverseRestoresSourceOrigin( &runtime, alloc, &wide_source, &narrow_source, case.positive_delta, case.endpoint_mode, ); }}
test "positive measured shrink retains split source origin without exact endpoint" { const alloc = std.testing.allocator; const flow = "line 00 abcdefghij\n" ++ "line 01 abcdefghij\n" ++ "line 02 abcdefghij\n" ++ "line 03 abcdefghij\n" ++ "line 04 abcdefghij\n" ++ "line 05 abcdefghij\n" ++ "line 06 abcdefghij\n" ++ "line 07 abcdefghij\n" ++ "line 08 abcdefghij\n" ++ "line 09 abcdefghij\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var wide_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer wide_prepared.deinit(alloc); var split_selection = wide_prepared.selection; split_selection.split_active = true; split_selection.split_prefix_lines = 1; split_selection.split_suffix_start_line = split_selection.start_line; const source_origin = preparedVisualOffsetForTest(&wide_prepared); try installStableAnchorForTest( &runtime, alloc, flow, split_selection, source_origin, wide_prepared.cursor.cursor_row, wide_prepared.cursor.cursor_col, 705, );
runtime.layout = transcriptTestLayout(12, 8, 4); runtime.resize_history_row_delta = 2; var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); const origin = narrow_prepared.measured_history_origin orelse return error.TestExpectedMeasuredHistoryOrigin; try std.testing.expectEqual(@as(u16, 24), origin.source_cols); try std.testing.expectEqual(source_origin, origin.source_visual_offset); try std.testing.expect(origin.endpoint == null);}
test "zero measured history controls do not consume a nonqualifying origin" { const alloc = std.testing.allocator; const flow = "line 00 abcdefghij\n" ++ "line 01 abcdefghij\n" ++ "line 02 abcdefghij\n" ++ "line 03 abcdefghij\n" ++ "line 04 abcdefghij\n" ++ "line 05 abcdefghij\n";
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(12, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); const offset = preparedVisualOffsetForTest(&prepared); try installStableAnchorForTest( &runtime, alloc, flow, prepared.selection, offset, prepared.cursor.cursor_row, prepared.cursor.cursor_col, 706, ); runtime.resize_history_row_delta = 0; var generic_zero = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer generic_zero.deinit(alloc); try std.testing.expectEqual(offset, preparedVisualOffsetForTest(&generic_zero)); try std.testing.expect(!generic_zero.measured_history_origin_consumed); }
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(12, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); const narrow_offset = preparedVisualOffsetForTest(&narrow_prepared); try installStableAnchorWithOriginForTest( &runtime, alloc, flow, narrow_prepared.selection, narrow_offset, narrow_prepared.cursor.cursor_row, narrow_prepared.cursor.cursor_col, 707, .{ .source_cols = 24, .source_visual_offset = 1, }, );
runtime.resize_history_row_delta = 0; var same_width = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer same_width.deinit(alloc); try std.testing.expectEqual( narrow_offset, preparedVisualOffsetForTest(&same_width), ); try std.testing.expect(!same_width.measured_history_origin_consumed);
runtime.layout = transcriptTestLayout(18, 10, 6); var middle_source = try testSource(alloc, flow, runtime.layout.cols); defer middle_source.deinit(alloc); var non_source_width = try prepareTestSourceForCurrentArea( &runtime, alloc, &middle_source, ); defer non_source_width.deinit(alloc); try std.testing.expect(!non_source_width.measured_history_origin_consumed); }}
test "negative measured rewind consumes the retained exact origin" { const alloc = std.testing.allocator; const flow = "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n" ++ "abcdefghijklmnopqrst\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var wide_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer wide_prepared.deinit(alloc); try setPreparedVisualOffsetForTest(&wide_prepared, 10); try installStableAnchorForTest( &runtime, alloc, flow, wide_prepared.selection, 10, wide_prepared.cursor.cursor_row, wide_prepared.cursor.cursor_col, 708, );
runtime.layout = transcriptTestLayout(12, 8, 4); runtime.resize_history_row_delta = 2; var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); const origin = narrow_prepared.measured_history_origin orelse return error.TestExpectedMeasuredHistoryOrigin; narrow_prepared.measured_history_origin = .{ .source_cols = origin.source_cols, .source_visual_offset = origin.source_visual_offset, .endpoint = .{ .span_start_flow_offset = 210, .flow_offset = 231, .hard_row_cols = 24, }, }; try std.testing.expectEqual(@as(u32, 22), preparedVisualOffsetForTest(&narrow_prepared)); try commitPreparedForTest(&runtime, alloc, &narrow_source, &narrow_prepared);
runtime.layout = transcriptTestLayout(18, 10, 6); runtime.resize_history_row_delta = -1; var rewind_source = try testSource(alloc, flow, runtime.layout.cols); defer rewind_source.deinit(alloc); var rewound = try prepareTestSourceForCurrentArea( &runtime, alloc, &rewind_source, ); defer rewound.deinit(alloc); try std.testing.expectEqual(@as(u32, 21), preparedVisualOffsetForTest(&rewound)); try std.testing.expect(rewound.measured_history_origin_consumed); try commitPreparedForTest(&runtime, alloc, &rewind_source, &rewound); const stable = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestExpectedStableTranscript; try std.testing.expect(stable.measured_history_origin == null);}
test "incompatible transcript replacement clears the retained origin" { const alloc = std.testing.allocator; const flow = "stable 0\nstable 1\nstable 2\nstable 3\nstable 4\nstable 5\n"; const replacement = "replacement 0\nreplacement 1\nreplacement 2\nreplacement 3\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(12, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); var stable_source = try testSource(alloc, flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try installStableAnchorWithOriginForTest( &runtime, alloc, flow, stable_prepared.selection, preparedVisualOffsetForTest(&stable_prepared), stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 709, .{ .source_cols = 24, .source_visual_offset = 2, }, );
var replacement_source = try testSource( alloc, replacement, runtime.layout.cols, ); defer replacement_source.deinit(alloc); var replacement_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &replacement_source, ); defer replacement_prepared.deinit(alloc); try commitPreparedForTest( &runtime, alloc, &replacement_source, &replacement_prepared, ); const stable = runtime.stableTranscriptProjectionForFlow(replacement) orelse return error.TestExpectedStableTranscript; try std.testing.expect(stable.measured_history_origin == null);}
test "zero measured reverse restores source origin from recovery receipt" { const alloc = std.testing.allocator; const flow = "line 00 abcdefghij\n" ++ "line 01 abcdefghij\n" ++ "line 02 abcdefghij\n" ++ "line 03 abcdefghij\n" ++ "line 04 abcdefghij\n" ++ "line 05 abcdefghij\n" ++ "line 06 abcdefghij\n" ++ "line 07 abcdefghij\n" ++ "line 08 abcdefghij\n" ++ "line 09 abcdefghij\n" ++ "line 10 abcdefghij\n" ++ "line 11 abcdefghij\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var wide_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer wide_prepared.deinit(alloc); const natural_origin = preparedVisualOffsetForTest(&wide_prepared); const source_origin = natural_origin + 2; try setPreparedVisualOffsetForTest(&wide_prepared, source_origin); try installStableAnchorForTest( &runtime, alloc, flow, wide_prepared.selection, source_origin, wide_prepared.cursor.cursor_row, wide_prepared.cursor.cursor_col, 710, );
runtime.layout = transcriptTestLayout(12, 8, 4); runtime.resize_history_row_delta = 2; var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); try commitPreparedPartiallyForTest( &runtime, alloc, &narrow_source, &narrow_prepared, ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null); const recovering_origin = runtime.measuredHistoryOriginForFlow(flow) orelse return error.TestExpectedMeasuredHistoryOrigin; try std.testing.expectEqual( source_origin, recovering_origin.origin.source_visual_offset, );
runtime.layout = transcriptTestLayout(24, 12, 8); runtime.resize_history_row_delta = 0; var restored = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer restored.deinit(alloc); try std.testing.expectEqual( source_origin, preparedVisualOffsetForTest(&restored), ); try commitPreparedForTest(&runtime, alloc, &wide_source, &restored); const recovered = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestExpectedStableTranscript; try std.testing.expectEqual(source_origin, recovered.visual_offset); try std.testing.expect(recovered.measured_history_origin == null); runtime.resize_history_row_delta = null; try expectRendererAppendRestored( &runtime, alloc, flow, flow ++ "recovered append 0\nrecovered append 1\nrecovered append 2\n" ++ "recovered append 3\nrecovered append 4\nrecovered append 5\n", );}
test "zero measured reverse restores source origin after compatible paint" { const alloc = std.testing.allocator; const flow = "line 00 abcdefghij\n" ++ "line 01 abcdefghij\n" ++ "line 02 abcdefghij\n" ++ "line 03 abcdefghij\n" ++ "line 04 abcdefghij\n" ++ "line 05 abcdefghij\n" ++ "line 06 abcdefghij\n" ++ "line 07 abcdefghij\n" ++ "line 08 abcdefghij\n" ++ "line 09 abcdefghij\n" ++ "line 10 abcdefghij\n" ++ "line 11 abcdefghij\n"; const grown_flow = flow ++ "compatible append\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var wide_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer wide_prepared.deinit(alloc); const source_origin = preparedVisualOffsetForTest(&wide_prepared) + 2; try setPreparedVisualOffsetForTest(&wide_prepared, source_origin); try installStableAnchorForTest( &runtime, alloc, flow, wide_prepared.selection, source_origin, wide_prepared.cursor.cursor_row, wide_prepared.cursor.cursor_col, 720, );
runtime.layout = transcriptTestLayout(12, 8, 4); runtime.resize_history_row_delta = 2; var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); try commitPreparedForTest( &runtime, alloc, &narrow_source, &narrow_prepared, );
runtime.resize_history_row_delta = null; var grown_narrow_source = try testSource( alloc, grown_flow, runtime.layout.cols, ); defer grown_narrow_source.deinit(alloc); var grown_narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &grown_narrow_source, ); defer grown_narrow_prepared.deinit(alloc); try commitPreparedForTest( &runtime, alloc, &grown_narrow_source, &grown_narrow_prepared, ); const compatible = runtime.stableTranscriptProjectionForFlow(grown_flow) orelse return error.TestExpectedStableTranscript; const compatible_origin = compatible.measured_history_origin orelse return error.TestExpectedMeasuredHistoryOrigin; try std.testing.expectEqual( source_origin, compatible_origin.source_visual_offset, );
runtime.layout = transcriptTestLayout(24, 12, 8); runtime.resize_history_row_delta = 0; var grown_wide_source = try testSource( alloc, grown_flow, runtime.layout.cols, ); defer grown_wide_source.deinit(alloc); var restored = try prepareTestSourceForCurrentArea( &runtime, alloc, &grown_wide_source, ); defer restored.deinit(alloc); try std.testing.expectEqual( source_origin, preparedVisualOffsetForTest(&restored), ); try commitPreparedForTest( &runtime, alloc, &grown_wide_source, &restored, ); const restored_projection = runtime.stableTranscriptProjectionForFlow( grown_flow, ) orelse return error.TestExpectedStableTranscript; try std.testing.expectEqual(source_origin, restored_projection.visual_offset); try std.testing.expect(restored_projection.measured_history_origin == null); runtime.resize_history_row_delta = null; try expectRendererAppendRestored( &runtime, alloc, grown_flow, grown_flow ++ "post-restore append 0\npost-restore append 1\n" ++ "post-restore append 2\npost-restore append 3\n" ++ "post-restore append 4\npost-restore append 5\n", );}
test "unrepresentable zero reverse rebases to natural source projection" { const alloc = std.testing.allocator; const flow = "line 00\n" ++ "line 01\n" ++ "line 02\n" ++ "line 03\n" ++ "line 04\n" ++ "line 05\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(12, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); var narrow_source = try testSource(alloc, flow, runtime.layout.cols); defer narrow_source.deinit(alloc); var narrow_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &narrow_source, ); defer narrow_prepared.deinit(alloc); try installStableAnchorWithOriginForTest( &runtime, alloc, flow, narrow_prepared.selection, preparedVisualOffsetForTest(&narrow_prepared), narrow_prepared.cursor.cursor_row, narrow_prepared.cursor.cursor_col, 730, .{ .source_cols = 24, .source_visual_offset = 99, }, );
runtime.layout = transcriptTestLayout(24, 12, 8); runtime.resize_history_row_delta = null; var wide_source = try testSource(alloc, flow, runtime.layout.cols); defer wide_source.deinit(alloc); var natural = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer natural.deinit(alloc); const natural_offset = preparedVisualOffsetForTest(&natural);
runtime.resize_history_row_delta = 0; var rebased = try prepareTestSourceForCurrentArea( &runtime, alloc, &wide_source, ); defer rebased.deinit(alloc); try std.testing.expectEqual( natural_offset, preparedVisualOffsetForTest(&rebased), ); try std.testing.expect(rebased.measured_history_origin_consumed);}
test "measured resize history excludes synthetic reflow fallback" { const alloc = std.testing.allocator; const flow = "stable 00\nstable 01\nstable 02\nstable 03\nstable 04\n" ++ "stable 05\nstable 06\nstable 07\nstable 08\nstable 09\n" ++ "stable 10\nstable 11\nstable 12\nstable 13\nstable 14\n" ++ "stable 15\nstable 16\nstable 17\nstable 18\nstable 19\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, flow, testSelectionInArea(0, 1, 4), 0, 4, 1, 46, );
const shadow = runtime.shadow_vt.?; shadow.cells[19].codepoint = 'x'; shadow.cursor_row = 8; shadow.cursor_col = 1; runtime.viewport_clear_pending = true; runtime.layout = transcriptTestLayout(10, 8, 2);
runtime.resize_history_row_delta = 2; var measured_source = try testSource(alloc, flow, runtime.layout.cols); defer measured_source.deinit(alloc); var measured_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &measured_source, ); defer measured_prepared.deinit(alloc); const measured = runtime.planTranscriptScroll(&measured_prepared); try std.testing.expect(measured.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), measured.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), measured.semantic_rows); try std.testing.expectEqual(@as(u16, 0), measured.planned_rows); if (measured_prepared.measured_history_origin == null) { return error.TestExpectedMeasuredHistoryOrigin; } const prepared_endpoint = measured_prepared.measured_history_origin.?.endpoint;
runtime.resize_history_row_delta = 0; var zero_source = try testSource(alloc, flow, runtime.layout.cols); defer zero_source.deinit(alloc); var zero_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &zero_source, ); defer zero_prepared.deinit(alloc); const zero = runtime.planTranscriptScroll(&zero_prepared); try std.testing.expect(zero.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), zero.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), zero.semantic_rows); try std.testing.expectEqual(@as(u16, 0), zero.planned_rows);
runtime.resize_history_row_delta = null; var fallback_source = try testSource(alloc, flow, runtime.layout.cols); defer fallback_source.deinit(alloc); var fallback_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &fallback_source, ); defer fallback_prepared.deinit(alloc); const fallback = runtime.planTranscriptScroll(&fallback_prepared); try std.testing.expect(fallback.recovery_rebase); try std.testing.expectEqual(@as(u16, 2), fallback.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), fallback.semantic_rows); try std.testing.expectEqual(@as(u16, 2), fallback.planned_rows);
runtime.resize_history_row_delta = 2; try commitPreparedForTest( &runtime, alloc, &measured_source, &measured_prepared, ); const committed = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestExpectedStableTranscript; try std.testing.expectEqualDeep( prepared_endpoint, committed.measured_history_origin.?.endpoint, );}
test "measured resize releases one physical boundary row" { const alloc = std.testing.allocator; const flow = "stable 00\nstable 01\nstable 02\nstable 03\nstable 04\n" ++ "stable 05\nstable 06\nstable 07\nstable 08\nstable 09\n" ++ "stable 10\nstable 11\nstable 12\nstable 13\nstable 14\n" ++ "stable 15\nstable 16\nstable 17\nstable 18\nstable 19\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, flow, testSelectionInArea(0, 1, 4), 0, 4, 1, 46, );
const shadow = runtime.shadow_vt.?; shadow.cells[19].codepoint = 'x'; shadow.cursor_row = 8; shadow.cursor_col = 1; runtime.viewport_clear_pending = true; runtime.layout = transcriptTestLayout(10, 8, 3); runtime.resize_history_row_delta = 2;
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&prepared);
try std.testing.expect(facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 1), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), facts.planned_rows);}
test "same-source semantic debt survives narrow to wide projection rebase" { const alloc = std.testing.allocator; const flow = "abcdefghijk\n" ++ "one\n" ++ "two\n" ++ "three\n" ++ "four\n" ++ "five\n"; const grown_flow = flow ++ "six\nseven\n"; const appended_flow = grown_flow ++ "eight\nnine\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(5, 10, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try enterRendererDerivedSemanticRecovery( &runtime, alloc, flow, 4, 1, null, ); const retained_semantic_rows = runtime.transcriptCommitDiagnostic().remaining_inline_rows; try std.testing.expect(retained_semantic_rows > 0);
runtime.layout = transcriptTestLayout(20, 10, 4); const first = try consumeRendererRecoveryAttempt( &runtime, alloc, flow, 0, .terminal_partial_write, ); try std.testing.expect(first.facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), first.facts.resize_reflow_rows); try std.testing.expectEqual(retained_semantic_rows, first.facts.semantic_rows); try std.testing.expectEqual(retained_semantic_rows, first.facts.planned_rows); try std.testing.expectEqual( retained_semantic_rows, runtime.transcriptCommitDiagnostic().remaining_inline_rows, ); try std.testing.expectEqual( first.facts.target_visual_offset, first.visual_offset, );
const growth = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 0, .terminal_partial_write, ); try std.testing.expect(growth.facts.recovery_rebase); try std.testing.expect(growth.facts.semantic_rows > retained_semantic_rows); const growth_rows = growth.facts.semantic_rows - retained_semantic_rows; try std.testing.expectEqual( first.visual_offset + growth_rows, growth.visual_offset, ); try std.testing.expect( runtime.stableTranscriptProjectionForFlow(grown_flow) == null, );
const repeated = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 0, .terminal_partial_write, ); try std.testing.expectEqual( growth.facts.semantic_rows, repeated.facts.semantic_rows, ); try std.testing.expectEqual(growth.visual_offset, repeated.visual_offset);
const complete = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, @intCast(repeated.facts.semantic_rows), .committed, ); try std.testing.expectEqual( repeated.facts.semantic_rows, complete.facts.semantic_rows, ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const stable = runtime.stableTranscriptProjectionForFlow(grown_flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(growth.visual_offset, stable.visual_offset); try expectRendererAppendRestored( &runtime, alloc, grown_flow, appended_flow, );}
test "same-source semantic and resize debt survive wide to narrow rebase in order" { const alloc = std.testing.allocator; const flow = "stable\nbase\n"; const grown_flow = flow ++ "growth one\ngrowth two\n"; const appended_flow = grown_flow ++ "after one\nafter two\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try enterResizeReflowRecoveryWithAcceptedRows( &runtime, alloc, null, .terminal_partial_write, 0, );
const growth = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 0, .terminal_partial_write, ); try std.testing.expectEqual(@as(u16, 2), growth.facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 2), growth.facts.semantic_rows);
runtime.layout = transcriptTestLayout(5, 8, 2); const first = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 1, .terminal_partial_write, ); try std.testing.expect(first.facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 2), first.facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 2), first.facts.semantic_rows); try std.testing.expectEqual(@as(u16, 4), first.facts.planned_rows); try std.testing.expectEqual( @as(u16, 3), runtime.transcriptCommitDiagnostic().remaining_inline_rows, );
const second = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 1, .terminal_partial_write, ); try std.testing.expectEqual(@as(u16, 1), second.facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 2), second.facts.semantic_rows); try std.testing.expectEqual( @as(u16, 2), runtime.transcriptCommitDiagnostic().remaining_inline_rows, ); try std.testing.expectEqual(first.visual_offset, second.visual_offset);
const third = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 1, .terminal_partial_write, ); try std.testing.expectEqual(@as(u16, 0), third.facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 2), third.facts.semantic_rows); try std.testing.expectEqual( @as(u16, 1), runtime.transcriptCommitDiagnostic().remaining_inline_rows, ); try std.testing.expectEqual(first.visual_offset, third.visual_offset);
const complete = try consumeRendererRecoveryAttempt( &runtime, alloc, grown_flow, 1, .committed, ); try std.testing.expectEqual(@as(u16, 0), complete.facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), complete.facts.semantic_rows); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const stable = runtime.stableTranscriptProjectionForFlow(grown_flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(first.visual_offset, stable.visual_offset); try expectRendererAppendRestored( &runtime, alloc, grown_flow, appended_flow, );}
test "same-source semantic debt survives measured history rebase" { const alloc = std.testing.allocator; const flow = "0\n1\n2\n3\n4\n5\n6\n7\n"; const grown_flow = flow ++ "8\n9\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try enterRendererDerivedSemanticRecovery( &runtime, alloc, flow, 4, 1, null, ); const retained_semantic_rows = runtime.transcriptCommitDiagnostic().remaining_inline_rows; try std.testing.expect(retained_semantic_rows > 1);
runtime.resize_history_row_delta = 2; const measured = try consumeRendererRecoveryAttempt( &runtime, alloc, flow, 0, .committed, ); try std.testing.expect(measured.facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 0), measured.facts.resize_reflow_rows); try std.testing.expectEqual( retained_semantic_rows, measured.facts.semantic_rows, ); try std.testing.expectEqual( retained_semantic_rows, runtime.transcriptCommitDiagnostic().remaining_inline_rows, );
runtime.resize_history_row_delta = null; const repeated = try consumeRendererRecoveryAttempt( &runtime, alloc, flow, 1, .terminal_partial_write, ); try std.testing.expectEqual( retained_semantic_rows, repeated.facts.semantic_rows, ); try std.testing.expectEqual( retained_semantic_rows - 1, runtime.transcriptCommitDiagnostic().remaining_inline_rows, ); try std.testing.expectEqual(measured.visual_offset, repeated.visual_offset); try std.testing.expect( runtime.stableTranscriptProjectionForFlow(flow) == null, );
const remaining = retained_semantic_rows - 1; const complete = try consumeRendererRecoveryAttempt( &runtime, alloc, flow, @intCast(remaining), .committed, ); try std.testing.expectEqual(remaining, complete.facts.semantic_rows); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const stable = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(measured.visual_offset, stable.visual_offset); try expectRendererAppendRestored(&runtime, alloc, flow, grown_flow);}
test "finalized transcript transition owns append suffix and commits one anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest(&runtime, alloc, "old\n", testSelection(0), 0, 3, 1, 43);
var source = try testSource(alloc, "old\nnew\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelection(1), .cursor = .{ .cursor_row = 4, .cursor_col = 1, .replaceable_row = 4 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &.{ 1, 1 }); const scroll_plan = render_engine.frame_scroll_plan.merge(runtime.layout.rows, 1, 0, 1); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqualStrings("new\r\n", transition.document_append.bytes); try std.testing.expectEqual(@as(u16, 3), transition.document_append.start_row); try std.testing.expectEqual(@as(u16, 1), transition.document_append.start_col);
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = false, .terminal_scroll_rows_committed = 1, .document_append_committed = true, .committed_cursor_row = 4, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual(transcript_runtime.TranscriptCommitDiagnosticState.stable, diagnostic.state); try std.testing.expectEqual(@as(?usize, 1), diagnostic.stable_start_line); try std.testing.expectEqual(@as(u32, 1), diagnostic.visual_offset); const stable = runtime.stableTranscriptProjectionForFlow("old\nnew\n").?; try std.testing.expectEqual(@as(u16, 4), stable.cursor_row); try std.testing.expectEqual(@as(u16, 1), stable.cursor_col);}
test "transcript transition staging preserves prior commit across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkTranscriptTransitionStagingAllocationFailures, .{}, );}
test "committed transcript transition installation performs no allocation" { const backing = std.testing.allocator; var failing = std.testing.FailingAllocator.init(backing, .{}); const alloc = failing.allocator(); var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest(&runtime, alloc, "old\n", testSelection(0), 0, 3, 1, 72);
var source = try testSource(alloc, "old\nnew\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelection(1), .cursor = .{ .cursor_row = 4, .cursor_col = 1, .replaceable_row = 4 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &.{ 1, 1 }); const scroll_plan = render_engine.frame_scroll_plan.merge(runtime.layout.rows, 1, 0, 1); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc);
failing.fail_index = failing.alloc_index; runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = false, .terminal_scroll_rows_committed = 1, .document_append_committed = true, .committed_cursor_row = 4, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
try std.testing.expect(!failing.has_induced_failure); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );}
test "partial transcript transition records exact recovery debt" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest(&runtime, alloc, "stable", testSelection(0), 0, 3, 1, 44);
var source = try testSource(alloc, "stable", runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelection(5), .cursor = .{ .cursor_row = 4, .cursor_col = 1, .replaceable_row = 4 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &([_]u16{1} ** 16)); const scroll_plan = render_engine.frame_scroll_plan.merge(runtime.layout.rows, 1, 0, 5); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc);
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = false, .terminal_scroll_rows_committed = 2, .document_append_committed = false, .committed_cursor_row = 3, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual(transcript_runtime.TranscriptCommitDiagnosticState.recovering, diagnostic.state); try std.testing.expectEqual(@as(u32, 2), diagnostic.visual_offset); try std.testing.expectEqual(@as(u16, 3), diagnostic.remaining_inline_rows); try std.testing.expect(runtime.stableTranscriptProjectionForFlow("stable") == null);}
test "transcript recovery debt does not double count unmaterialized inline rows" { const alloc = std.testing.allocator; const flow = "00\n01\n02\n03\n04\n05\n06\n07\n" ++ "08\n09\n10\n11\n12\n13\n14\n15"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var stable_source = try testSource(alloc, flow, runtime.layout.cols); defer stable_source.deinit(alloc); var stable_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &stable_source, ); defer stable_prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 8), stable_prepared.selection.start_line); try installStableAnchorForTest( &runtime, alloc, flow, stable_prepared.selection, 8, stable_prepared.cursor.cursor_row, stable_prepared.cursor.cursor_col, 44, );
runtime.owned_top_row = 5; var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 12), prepared.selection.start_line); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 4), facts.semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge(runtime.layout.rows, 5, 3, 4); try std.testing.expectEqual(@as(u16, 1), scroll_plan.remaining_inline_advance_rows); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc);
runtime.ackPreservedRowReleaseAssumeValid( scroll_plan, scroll_plan.terminal_scroll_rows, ); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = false, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = prepared.cursor.cursor_row, .committed_cursor_col = prepared.cursor.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual(transcript_runtime.TranscriptCommitDiagnosticState.recovering, diagnostic.state); try std.testing.expectEqual(@as(u16, 1), diagnostic.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 11), diagnostic.visual_offset);
var recovery_source = try testSource(alloc, flow, runtime.layout.cols); defer recovery_source.deinit(alloc); var recovery_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &recovery_source, ); defer recovery_prepared.deinit(alloc); const recovery_facts = runtime.planTranscriptScroll(&recovery_prepared); try std.testing.expectEqual(@as(u16, 1), recovery_facts.planned_rows); const recovery_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, recovery_facts.planned_rows, ); var recovery_plan = testPaintPlan(&runtime, recovery_prepared.selection); const recovery_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(recovery_plan); var recovery_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &recovery_source, &recovery_prepared, recovery_layout, &recovery_plan, recovery_scroll_plan, ); defer recovery_transition.deinit(alloc); try std.testing.expect(recovery_transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &recovery_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 1, .document_append_committed = false, .committed_cursor_row = recovery_transition.cursor_row, .committed_cursor_col = recovery_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); const settled = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual(transcript_runtime.TranscriptCommitDiagnosticState.stable, settled.state); try std.testing.expectEqual(@as(u16, 0), settled.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 12), settled.visual_offset);}
test "recovery area-only endpoint movement preserves semantic debt" { const alloc = std.testing.allocator; const stable_flow = "stable\n"; const attempt_flow = stable_flow ++ "attempt\n"; const attempt_rows = [_]u16{1} ** 10; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 5, }; defer runtime.deinit(alloc); try enterSemanticAppendRecovery( &runtime, alloc, stable_flow, attempt_flow, &attempt_rows, );
var source = try testSource(alloc, attempt_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelectionInArea(7, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &attempt_rows);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u16, 0), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), facts.planned_rows);}
test "recovery source growth plus area movement counts only source visual growth" { const alloc = std.testing.allocator; const stable_flow = "stable\n"; const attempt_flow = stable_flow ++ "attempt\n"; const grown_flow = attempt_flow ++ "growth one\ngrowth two\n"; const attempt_rows = [_]u16{1} ** 10; const grown_rows = [_]u16{1} ** 12; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 5, }; defer runtime.deinit(alloc); try enterSemanticAppendRecovery( &runtime, alloc, stable_flow, attempt_flow, &attempt_rows, );
var source = try testSource(alloc, grown_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = source.bytes, .owns_bytes = false, .selection = testSelectionInArea(9, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer prepared.deinit(alloc); try prepared.line_visual_rows.appendSlice(alloc, &grown_rows);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u32, 3), facts.source_visual_offset); try std.testing.expectEqual(@as(u32, 9), facts.target_visual_offset); try std.testing.expectEqual(@as(u16, 0), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 3), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 3), facts.planned_rows);}
test "recovery consumes resize reflow before semantic debt across partial results" { const alloc = std.testing.allocator; const grown_flow = "stable\nbase\n" ++ "growth one\ngrowth two\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try enterResizeReflowRecovery( &runtime, alloc, null, .terminal_partial_write, );
var source = try testSource(alloc, grown_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u16, 1), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 2), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 3), facts.planned_rows);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 2, .document_append_committed = false, .committed_cursor_row = 2, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u32, 1), recovery.visual_offset); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);
var followup_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer followup_source.deinit(alloc); var followup_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &followup_source, ); defer followup_prepared.deinit(alloc); const followup_facts = runtime.planTranscriptScroll(&followup_prepared); try std.testing.expectEqual(@as(u16, 0), followup_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), followup_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), followup_facts.planned_rows);}
test "committed resize cleanup retains fallback recovery reflow debt" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try enterResizeReflowRecovery( &runtime, alloc, null, .committed, ); const recovery_offset = runtime.transcriptCommitDiagnostic().visual_offset;
runtime.resize_history_row_delta = null; var source = try testSource(alloc, "stable\nbase\n", runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 1), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), facts.planned_rows);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, target_layout, &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 0, .changed_cells = 0, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = 2, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(recovery_offset, recovery.visual_offset); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);
var complete_source = try testSource(alloc, "stable\nbase\n", runtime.layout.cols); defer complete_source.deinit(alloc); var complete_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &complete_source, ); defer complete_prepared.deinit(alloc); const complete_facts = runtime.planTranscriptScroll(&complete_prepared); try std.testing.expect(complete_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 1), complete_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), complete_facts.semantic_rows); const complete_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, complete_facts.planned_rows, ); var complete_plan = testPaintPlan(&runtime, complete_prepared.selection); var complete_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &complete_source, &complete_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(complete_plan), &complete_plan, complete_scroll_plan, ); defer complete_transition.deinit(alloc); try std.testing.expectEqual(recovery_offset, complete_transition.visual_offset); runtime.consumeTranscriptTransition(alloc, &complete_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = complete_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = complete_transition.cursor_row, .committed_cursor_col = complete_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const stable = runtime.stableTranscriptProjectionForFlow("stable\nbase\n") orelse return error.TestUnexpectedResult; try std.testing.expectEqual(recovery_offset, stable.visual_offset);}
test "incompatible recovery source stays on rebase path after partial repaint" { const alloc = std.testing.allocator; const stable_flow = "stable\n"; const attempt_flow = stable_flow ++ "attempt\n"; const replacement_flow = "replacement\n"; const grown_replacement_flow = replacement_flow ++ "growth\n"; const attempt_rows = [_]u16{1} ** 10; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 5, }; defer runtime.deinit(alloc); try enterSemanticAppendRecovery( &runtime, alloc, stable_flow, attempt_flow, &attempt_rows, );
var replacement_source = try testSource( alloc, replacement_flow, runtime.layout.cols, ); defer replacement_source.deinit(alloc); var replacement_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = replacement_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(0, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer replacement_prepared.deinit(alloc); try replacement_prepared.line_visual_rows.appendSlice( alloc, &.{ 1, 1, 1, 1, 1 }, ); const replacement_facts = runtime.planTranscriptScroll(&replacement_prepared); try std.testing.expect(!replacement_facts.source_compatible); try std.testing.expectEqual(@as(u16, 0), replacement_facts.planned_rows);
const replacement_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, replacement_facts.planned_rows, ); var replacement_plan = testPaintPlan(&runtime, replacement_prepared.selection); var replacement_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &replacement_source, &replacement_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( replacement_plan, ), &replacement_plan, replacement_scroll_plan, ); defer replacement_transition.deinit(alloc); try std.testing.expect(replacement_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &replacement_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = 6, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, runtime.transcriptCommitDiagnostic().state, );
var grown_source = try testSource( alloc, grown_replacement_flow, runtime.layout.cols, ); defer grown_source.deinit(alloc); var grown_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = grown_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(1, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer grown_prepared.deinit(alloc); try grown_prepared.line_visual_rows.appendSlice( alloc, &.{ 1, 1, 1, 1, 1, 1 }, ); const grown_facts = runtime.planTranscriptScroll(&grown_prepared); try std.testing.expect(grown_facts.source_compatible); try std.testing.expectEqual(@as(u16, 0), grown_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), grown_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), grown_facts.planned_rows);}
test "structured replacement preserves reflow debt and drops old semantic debt" { const alloc = std.testing.allocator; const stable_flow = "stable\nbase\n"; const old_growth = "old growth\n"; const replacement = "replacement\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntry(alloc, stable_flow); try enterResizeReflowRecoveryWithAcceptedRows( &runtime, alloc, null, .terminal_partial_write, 0, ); const growth_entry_id = try runtime.appendRawTranscriptEntry( alloc, old_growth, );
var old_source = try runtime.prepareTranscriptSource(alloc, null); defer old_source.deinit(alloc); var old_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &old_source, ); defer old_prepared.deinit(alloc); const old_facts = runtime.planTranscriptScroll(&old_prepared); try std.testing.expectEqual(@as(u16, 2), old_facts.resize_reflow_rows); try std.testing.expect(old_facts.semantic_rows > 0); try std.testing.expectEqual( old_facts.resize_reflow_rows + old_facts.semantic_rows, old_facts.planned_rows, );
const old_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, old_facts.planned_rows, ); var old_plan = testPaintPlan(&runtime, old_prepared.selection); var old_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &old_source, &old_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(old_plan), &old_plan, old_scroll_plan, ); defer old_transition.deinit(alloc); try std.testing.expect(old_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &old_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = old_transition.cursor_row, .committed_cursor_col = old_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( old_facts.planned_rows, runtime.transcriptCommitDiagnostic().remaining_inline_rows, );
try std.testing.expect(try runtime.updateRawBytesEntry( alloc, growth_entry_id, replacement, )); var recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 2), recovery.remaining_inline_rows); try std.testing.expect( std.mem.find(u8, runtime.transcript.items, replacement) != null, );
var replacement_source = try runtime.prepareTranscriptSource(alloc, null); defer replacement_source.deinit(alloc); var replacement_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &replacement_source, ); defer replacement_prepared.deinit(alloc); const replacement_facts = runtime.planTranscriptScroll(&replacement_prepared); try std.testing.expect(!replacement_facts.source_compatible); try std.testing.expect(replacement_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 2), replacement_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), replacement_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 2), replacement_facts.planned_rows);
const replacement_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, replacement_facts.planned_rows, ); var replacement_plan = testPaintPlan( &runtime, replacement_prepared.selection, ); var replacement_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &replacement_source, &replacement_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( replacement_plan, ), &replacement_plan, replacement_scroll_plan, ); defer replacement_transition.deinit(alloc); try std.testing.expect(replacement_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &replacement_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 1, .document_append_committed = false, .committed_cursor_row = replacement_transition.cursor_row, .committed_cursor_col = replacement_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);
var repeated_source = try runtime.prepareTranscriptSource(alloc, null); defer repeated_source.deinit(alloc); var repeated_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &repeated_source, ); defer repeated_prepared.deinit(alloc); const repeated_facts = runtime.planTranscriptScroll(&repeated_prepared); try std.testing.expect(repeated_facts.source_compatible); try std.testing.expect(repeated_facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 1), repeated_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.semantic_rows); const repeated_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, repeated_facts.planned_rows, ); var repeated_plan = testPaintPlan(&runtime, repeated_prepared.selection); var repeated_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &repeated_source, &repeated_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( repeated_plan, ), &repeated_plan, repeated_scroll_plan, ); defer repeated_transition.deinit(alloc); try std.testing.expect(repeated_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &repeated_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = repeated_transition.cursor_row, .committed_cursor_col = repeated_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( @as(u16, 1), runtime.transcriptCommitDiagnostic().remaining_inline_rows, );
var complete_source = try runtime.prepareTranscriptSource(alloc, null); defer complete_source.deinit(alloc); var complete_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &complete_source, ); defer complete_prepared.deinit(alloc); const complete_facts = runtime.planTranscriptScroll(&complete_prepared); try std.testing.expectEqual(@as(u16, 1), complete_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), complete_facts.semantic_rows); const complete_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, complete_facts.planned_rows, ); var complete_plan = testPaintPlan(&runtime, complete_prepared.selection); var complete_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &complete_source, &complete_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( complete_plan, ), &complete_plan, complete_scroll_plan, ); defer complete_transition.deinit(alloc); try std.testing.expect(complete_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &complete_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = complete_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = complete_transition.cursor_row, .committed_cursor_col = complete_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); var stable_replacement_source = try runtime.prepareTranscriptSource( alloc, null, ); defer stable_replacement_source.deinit(alloc); try std.testing.expect( runtime.stableTranscriptProjectionForFlow( stable_replacement_source.bytes, ) != null, ); const replacement_flow = try alloc.dupe( u8, stable_replacement_source.bytes, ); defer alloc.free(replacement_flow);
const append = "after\n"; _ = try runtime.appendRawTranscriptEntry(alloc, append); var append_source = try runtime.prepareTranscriptSource(alloc, null); defer append_source.deinit(alloc); var append_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &append_source, ); defer append_prepared.deinit(alloc); const append_facts = runtime.planTranscriptScroll(&append_prepared); try std.testing.expect(!append_facts.recovery_rebase); try std.testing.expect(append_facts.semantic_rows > 0); const append_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, append_facts.planned_rows, ); var append_plan = testPaintPlan(&runtime, append_prepared.selection); var append_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &append_source, &append_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan( append_plan, ), &append_plan, append_scroll_plan, ); defer append_transition.deinit(alloc); try std.testing.expect(append_source.bytes.len == 0); try std.testing.expect(append_transition.target_flow.len > replacement_flow.len); try expectDocumentWireEqualsLogical( alloc, append_transition.target_flow[replacement_flow.len..], append_transition.document_append.bytes, );}
test "structured retention preserves recovery reflow debt" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntry(alloc, "stable\n"); _ = try runtime.appendRawTranscriptEntry(alloc, "base\n"); try enterResizeReflowRecoveryWithAcceptedRows( &runtime, alloc, null, .terminal_partial_write, 0, );
runtime.max_retained_transcript_bytes = "base\n".len; try runtime.enforceStructuredRetention(alloc, null); try std.testing.expectEqualStrings("base\n", runtime.transcript.items); const recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 2), recovery.remaining_inline_rows);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &source, ); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(!facts.source_compatible); try std.testing.expect(facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 2), facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 2), facts.planned_rows);}
test "invalid origin partial recovery rebases until complete repaint then appends" { const alloc = std.testing.allocator; const flow = "0\n1\n2\n3\n4\n5\n6\n7"; const grown_flow = flow ++ "\n8\n9"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var first_source = try testSource(alloc, flow, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &first_source, ); defer first_prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 4), first_prepared.selection.start_line);
const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expect(first_facts.recovery_rebase); try std.testing.expect(!first_facts.recovery_projection_deferred); try std.testing.expectEqual(@as(u16, 0), first_facts.planned_rows); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc); try std.testing.expect(first_transition.recovery_rebase); try std.testing.expect(first_transition.document_append.isEmpty()); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, first_plan, &first_prepared, &first_transition, "4", );
runtime.consumeTranscriptTransition(alloc, &first_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = first_transition.cursor_row, .committed_cursor_col = first_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); var recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 0), recovery.remaining_inline_rows); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null);
var repeated_source = try testSource(alloc, flow, runtime.layout.cols); defer repeated_source.deinit(alloc); var repeated_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &repeated_source, ); defer repeated_prepared.deinit(alloc); const repeated_facts = runtime.planTranscriptScroll(&repeated_prepared); try std.testing.expect(repeated_facts.source_compatible); try std.testing.expect(repeated_facts.recovery_rebase); try std.testing.expect(!repeated_facts.recovery_projection_deferred); try std.testing.expectEqual(@as(u16, 0), repeated_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), repeated_facts.planned_rows);
const repeated_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, repeated_facts.planned_rows, ); var repeated_plan = testPaintPlan(&runtime, repeated_prepared.selection); var repeated_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &repeated_source, &repeated_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(repeated_plan), &repeated_plan, repeated_scroll_plan, ); defer repeated_transition.deinit(alloc); try std.testing.expect(repeated_transition.recovery_rebase); try std.testing.expect(repeated_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &repeated_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = repeated_transition.cursor_row, .committed_cursor_col = repeated_transition.cursor_col, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u16, 0), recovery.remaining_inline_rows); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(flow) == null);
var complete_source = try testSource(alloc, flow, runtime.layout.cols); defer complete_source.deinit(alloc); var complete_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &complete_source, ); defer complete_prepared.deinit(alloc); const complete_facts = runtime.planTranscriptScroll(&complete_prepared); try std.testing.expect(complete_facts.recovery_rebase); try std.testing.expect(!complete_facts.recovery_projection_deferred); const complete_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, complete_facts.planned_rows, ); var complete_plan = testPaintPlan(&runtime, complete_prepared.selection); var complete_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &complete_source, &complete_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(complete_plan), &complete_plan, complete_scroll_plan, ); defer complete_transition.deinit(alloc); try std.testing.expect(complete_transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &complete_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = complete_transition.cursor_row, .committed_cursor_col = complete_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const settled = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, settled.state, ); try std.testing.expectEqual(@as(u16, 0), settled.remaining_inline_rows); const stable = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(@as(usize, 4), stable.selection.start_line); try std.testing.expectEqual(@as(u32, 4), stable.visual_offset);
var append_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer append_source.deinit(alloc); var append_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &append_source, ); defer append_prepared.deinit(alloc); const append_facts = runtime.planTranscriptScroll(&append_prepared); try std.testing.expect(append_facts.source_compatible); try std.testing.expectEqual(@as(u16, 2), append_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 2), append_facts.planned_rows); const append_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, append_facts.planned_rows, ); var append_plan = testPaintPlan(&runtime, append_prepared.selection); var append_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &append_source, &append_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(append_plan), &append_plan, append_scroll_plan, ); defer append_transition.deinit(alloc); try expectDocumentWireEqualsLogical( alloc, grown_flow[flow.len..], append_transition.document_append.bytes, ); try std.testing.expectEqual( stable.cursor_row, append_transition.document_append.start_row, ); try std.testing.expectEqual( stable.cursor_col, append_transition.document_append.start_col, );}
test "recovery projection paints wrapped partial blank tail from acknowledged endpoint" { const alloc = std.testing.allocator; const flow = "abcdefghijklmno\nalpha\n\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(5, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const attempt_offset = try enterRendererDerivedSemanticRecovery( &runtime, alloc, flow, 3, 1, null, ); try std.testing.expectEqual(@as(u32, 2), attempt_offset); runtime.layout = transcriptTestLayout(5, 10, 6);
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 0), prepared.selection.partial_skip_rows);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqual(@as(u32, 2), transition.visual_offset); try std.testing.expectEqual(@as(usize, 0), transition.selection.start_line); try std.testing.expectEqual(@as(u16, 2), transition.selection.partial_skip_rows); try std.testing.expectEqual(@as(u16, 3), transition.selection.last_visible_row); try std.testing.expect(transition.selection.last_visible_row_blank); try std.testing.expectEqual(@as(u16, 4), transition.cursor_row); try std.testing.expectEqual(@as(u16, 1), transition.cursor_col); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, plan, &prepared, &transition, "klmno", );}
test "recovery projection moves cursor for zero-row trailing newline growth" { const alloc = std.testing.allocator; const attempt_flow = "a\nb\nc"; const grown_flow = attempt_flow ++ "\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const attempt_offset = try enterRendererDerivedSemanticRecovery( &runtime, alloc, attempt_flow, 2, 0, null, ); try std.testing.expectEqual(@as(u32, 1), attempt_offset); runtime.layout = transcriptTestLayout(20, 8, 3);
var source = try testSource(alloc, grown_flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); try std.testing.expectEqual(@as(u32, 0), facts.target_visual_offset); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqual(@as(u32, 1), transition.visual_offset); try std.testing.expectEqual(@as(usize, 1), transition.selection.start_line); try std.testing.expectEqual(@as(u16, 2), transition.selection.last_visible_row); try std.testing.expectEqual(@as(u16, 3), transition.cursor_row); try std.testing.expectEqual(@as(u16, 1), transition.cursor_col); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, plan, &prepared, &transition, "b", );}
test "recovery projection relocates replaceable row through production painter" { const alloc = std.testing.allocator; const flow = "a\nb\nc\nreplace"; const replaceable_start = std.mem.lastIndexOfScalar(u8, flow, '\n').? + 1; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 5), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const attempt_offset = try enterRendererDerivedSemanticRecovery( &runtime, alloc, flow, 2, 1, replaceable_start, ); try std.testing.expectEqual(@as(u32, 2), attempt_offset); runtime.layout = transcriptTestLayout(20, 8, 6);
var source = try testSourceWithReplaceableTail( alloc, flow, runtime.layout.cols, replaceable_start, ); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(u16, 4), prepared.cursor.replaceable_row);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqual(@as(u32, 2), transition.visual_offset); try std.testing.expectEqual(@as(usize, 2), transition.selection.start_line); try std.testing.expectEqual(@as(u16, 2), transition.selection.replaceable_start_row); try std.testing.expectEqual(@as(u16, 2), transition.replaceable_row); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, plan, &prepared, &transition, "c", );}
test "row-only shrink defers wrapped recovery projection and later converges" { const alloc = std.testing.allocator; const flow = "abcdefghijklmno\nalpha\n\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(5, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const attempt_offset = try enterRendererDerivedSemanticRecovery( &runtime, alloc, flow, 3, 1, null, ); try std.testing.expectEqual(@as(u32, 2), attempt_offset); runtime.layout = transcriptTestLayout(5, 10, 2);
var source = try testSource(alloc, flow, runtime.layout.cols); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 0), prepared.selection.partial_skip_rows); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expectEqual(@as(u16, 1), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), facts.planned_rows); try std.testing.expect(facts.recovery_projection_deferred); const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty()); try std.testing.expect(transition.recovery_projection_deferred); try std.testing.expectEqual(@as(u32, 3), transition.visual_offset); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, plan, &prepared, &transition, "alpha", );
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); var diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, diagnostic.state, ); try std.testing.expectEqual(@as(u16, 1), diagnostic.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 1), diagnostic.visual_offset);
runtime.layout = transcriptTestLayout(5, 10, 3); var recovery_source = try testSource(alloc, flow, runtime.layout.cols); defer recovery_source.deinit(alloc); var recovery_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &recovery_source, ); defer recovery_prepared.deinit(alloc); const recovery_facts = runtime.planTranscriptScroll(&recovery_prepared); try std.testing.expectEqual(@as(u16, 1), recovery_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), recovery_facts.planned_rows); try std.testing.expect(!recovery_facts.recovery_projection_deferred); const recovery_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, recovery_facts.planned_rows, ); var recovery_plan = testPaintPlan(&runtime, recovery_prepared.selection); var recovery_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &recovery_source, &recovery_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(recovery_plan), &recovery_plan, recovery_scroll_plan, ); defer recovery_transition.deinit(alloc); try std.testing.expect(recovery_transition.document_append.isEmpty()); try std.testing.expect(!recovery_transition.recovery_projection_deferred); try std.testing.expectEqual(@as(u32, 2), recovery_transition.visual_offset); try std.testing.expectEqual(@as(usize, 0), recovery_transition.selection.start_line); try std.testing.expectEqual(@as(u16, 2), recovery_transition.selection.partial_skip_rows); try expectFinalizedRecoveryProjectionPaintsCoherently( &runtime, alloc, recovery_plan, &recovery_prepared, &recovery_transition, "klmno", );
runtime.consumeTranscriptTransition(alloc, &recovery_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = recovery_scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = recovery_transition.cursor_row, .committed_cursor_col = recovery_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, diagnostic.state, ); try std.testing.expectEqual(@as(u16, 0), diagnostic.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 2), diagnostic.visual_offset); const projection = runtime.stableTranscriptProjectionForFlow(flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(@as(usize, 0), projection.selection.start_line); try std.testing.expectEqual(@as(u16, 2), projection.selection.partial_skip_rows); try std.testing.expectEqual(@as(u16, 3), projection.cursor_row); try std.testing.expectEqual(@as(u16, 1), projection.cursor_col);}
test "committed deferred shrink installs target geometry before recovery stabilizes" { const alloc = std.testing.allocator; const stable_flow = "0\n1\n2\n3"; const attempt_flow = stable_flow ++ "\n4\n5\n6\n7\n8\n9"; const visual_rows = [_]u16{1} ** 10;
var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile(std.testing.io, "deferred-layout.log", .{ .read = true }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = transcriptTestLayout(5, 10, 8), .owned_top_row = 5, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, stable_flow, testSelectionInArea(0, 5, 8), 0, 8, 1, 401, ); const stable_layout = runtime.committed_frame_layout;
var attempt_source = try testSource(alloc, attempt_flow, runtime.layout.cols); defer attempt_source.deinit(alloc); var attempt_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = attempt_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(6, 5, 8), .cursor = .{ .cursor_row = 8, .cursor_col = 1, .replaceable_row = 8 }, }; defer attempt_prepared.deinit(alloc); try attempt_prepared.line_visual_rows.appendSlice(alloc, &visual_rows); try makeSyntheticPreparedRenderable(&attempt_prepared, alloc);
const attempt_facts = runtime.planTranscriptScroll(&attempt_prepared); try std.testing.expectEqual(@as(u16, 6), attempt_facts.semantic_rows); const attempt_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, attempt_facts.planned_rows, ); var attempt_plan = testPaintPlan(&runtime, attempt_prepared.selection); var attempt_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &attempt_source, &attempt_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(attempt_plan), &attempt_plan, attempt_scroll_plan, ); defer attempt_transition.deinit(alloc);
runtime.ackPreservedRowReleaseAssumeValid(attempt_scroll_plan, 5); runtime.consumeTranscriptTransition(alloc, &attempt_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 5, .document_append_committed = true, .committed_cursor_row = 8, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqualDeep(stable_layout, runtime.committed_frame_layout); try std.testing.expectEqual(@as(u16, 1), runtime.owned_top_row); const partial_recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, partial_recovery.state, ); try std.testing.expectEqual(@as(u16, 5), partial_recovery.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 1), partial_recovery.visual_offset);
runtime.layout = transcriptTestLayout(5, 10, 2); var deferred_source = try testSource(alloc, attempt_flow, runtime.layout.cols); defer deferred_source.deinit(alloc); var deferred_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &deferred_source, ); defer deferred_prepared.deinit(alloc);
const deferred_facts = runtime.planTranscriptScroll(&deferred_prepared); try std.testing.expectEqual(@as(u16, 5), deferred_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), deferred_facts.planned_rows); try std.testing.expect(deferred_facts.recovery_projection_deferred); const deferred_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, deferred_facts.planned_rows, ); var deferred_plan = testPaintPlan(&runtime, deferred_prepared.selection); const target_layout = render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(deferred_plan); var deferred_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &deferred_source, &deferred_prepared, target_layout, &deferred_plan, deferred_scroll_plan, ); defer deferred_transition.deinit(alloc); try std.testing.expect(deferred_transition.document_append.isEmpty());
runtime.consumeTranscriptTransition(alloc, &deferred_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = deferred_transition.cursor_row, .committed_cursor_col = deferred_transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const recovering = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovering.state, ); try std.testing.expectEqual(@as(u16, 5), recovering.remaining_inline_rows); try std.testing.expectEqual(@as(u32, 1), recovering.visual_offset); try std.testing.expectEqualStrings(stable_flow, runtime.transcript.items); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(attempt_flow) == null);
var invalidation_probe = OwnedBandInvalidationProbe{}; var metrics: Metrics = .{}; const next_result = try render_engine.frame_builder.buildAndFlushFrame( alloc, &runtime, &metrics, .{ .plan = deferred_plan, .scroll_plan = render_engine.frame_scroll_plan.FrameScrollPlan.none( runtime.layout.rows, runtime.owned_top_row, ), .body_painter = .{ .ctx = &invalidation_probe, .paint = OwnedBandInvalidationProbe.paint, }, }, ); try std.testing.expectEqual( render_engine.terminal_diff.ShadowCommitState.committed, next_result.shadow_state, ); try std.testing.expectEqualDeep(target_layout, runtime.committed_frame_layout); try std.testing.expect(!invalidation_probe.saw_owned_band_change);}
test "recovery forward area movement stabilizes at acknowledged semantic endpoint" { const alloc = std.testing.allocator; const stable_flow = "stable\n"; const attempt_flow = stable_flow ++ "attempt\n"; const grown_flow = attempt_flow ++ "growth one\ngrowth two\n"; const attempt_rows = [_]u16{1} ** 10; const grown_rows = [_]u16{1} ** 12; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 5, }; defer runtime.deinit(alloc); try enterSemanticAppendRecovery( &runtime, alloc, stable_flow, attempt_flow, &attempt_rows, );
var first_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer first_source.deinit(alloc); var first_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = first_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(6, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer first_prepared.deinit(alloc); try first_prepared.line_visual_rows.appendSlice(alloc, &grown_rows); try makeSyntheticPreparedRenderable(&first_prepared, alloc); const first_facts = runtime.planTranscriptScroll(&first_prepared); try std.testing.expectEqual(@as(u16, 3), first_facts.semantic_rows); const first_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, first_facts.planned_rows, ); var first_plan = testPaintPlan(&runtime, first_prepared.selection); var first_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &first_source, &first_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(first_plan), &first_plan, first_scroll_plan, ); defer first_transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &first_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 1, .document_append_committed = false, .committed_cursor_row = 6, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); var recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u32, 4), recovery.visual_offset); try std.testing.expectEqual(@as(u16, 2), recovery.remaining_inline_rows);
var second_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer second_source.deinit(alloc); var second_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = second_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(8, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 6, .cursor_col = 1, .replaceable_row = 6 }, }; defer second_prepared.deinit(alloc); try second_prepared.line_visual_rows.appendSlice(alloc, &grown_rows); try makeSyntheticPreparedRenderable(&second_prepared, alloc); const second_facts = runtime.planTranscriptScroll(&second_prepared); try std.testing.expectEqual(@as(u16, 2), second_facts.semantic_rows); const second_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, second_facts.planned_rows, ); var second_plan = testPaintPlan(&runtime, second_prepared.selection); var second_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &second_source, &second_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(second_plan), &second_plan, second_scroll_plan, ); defer second_transition.deinit(alloc); runtime.consumeTranscriptTransition(alloc, &second_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = 1, .document_append_committed = false, .committed_cursor_row = 6, .committed_cursor_col = 1, .shadow_state = .terminal_partial_write, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); recovery = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.recovering, recovery.state, ); try std.testing.expectEqual(@as(u32, 5), recovery.visual_offset); try std.testing.expectEqual(@as(u16, 1), recovery.remaining_inline_rows);
var final_source = try testSource(alloc, grown_flow, runtime.layout.cols); defer final_source.deinit(alloc); var final_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = final_source.bytes, .owns_bytes = false, .selection = testSelectionInArea(7, 1, runtime.layout.content_bottom), .cursor = .{ .cursor_row = 5, .cursor_col = 1, .replaceable_row = 5 }, }; defer final_prepared.deinit(alloc); try final_prepared.line_visual_rows.appendSlice(alloc, &grown_rows); try makeSyntheticPreparedRenderable(&final_prepared, alloc); const final_facts = runtime.planTranscriptScroll(&final_prepared); try std.testing.expectEqual(@as(u16, 1), final_facts.semantic_rows); const final_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, final_facts.planned_rows, ); var final_plan = testPaintPlan(&runtime, final_prepared.selection); var final_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &final_source, &final_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(final_plan), &final_plan, final_scroll_plan, ); defer final_transition.deinit(alloc); try std.testing.expectEqual(@as(u32, 6), final_transition.visual_offset); try std.testing.expectEqual(@as(usize, 6), final_transition.selection.start_line); try std.testing.expectEqual(@as(u16, 6), final_transition.selection.last_visible_row); try std.testing.expectEqual(@as(u16, 6), final_transition.cursor_row); try std.testing.expectEqual(@as(u16, 1), final_transition.cursor_col); try std.testing.expectEqual(@as(usize, 6), final_plan.viewport.start_line); try std.testing.expectEqual(@as(u16, 6), final_plan.viewport.last_visible_row); try std.testing.expectEqual(@as(u16, 6), final_plan.cursor_target.?.row); try std.testing.expectEqual(@as(u16, 1), final_plan.cursor_target.?.col); final_prepared.selection = final_transition.selection; runtime.consumeTranscriptTransition(alloc, &final_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = final_scroll_plan.terminal_scroll_rows, .document_append_committed = !final_transition.document_append.isEmpty(), .committed_cursor_row = 6, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const settled = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, settled.state, ); try std.testing.expectEqual(@as(u32, 6), settled.visual_offset); const projection = runtime.stableTranscriptProjectionForFlow(grown_flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(@as(usize, 6), projection.selection.start_line); try std.testing.expectEqual(@as(u16, 6), projection.occupied_last_row); try std.testing.expectEqual(@as(u16, 6), projection.cursor_row); try std.testing.expectEqual(@as(u16, 1), projection.cursor_col); try std.testing.expectEqual( @as(u16, 0), runtime.planTranscriptScroll(&final_prepared).planned_rows, );}
test "recovery backward area movement stabilizes at acknowledged semantic endpoint" { const alloc = std.testing.allocator; const stable_flow = "stable\n"; const attempt_flow = stable_flow ++ "attempt\n"; const attempt_rows = [_]u16{1} ** 10; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 10, 6), .owned_top_row = 5, }; defer runtime.deinit(alloc); try enterSemanticAppendRecovery( &runtime, alloc, stable_flow, attempt_flow, &attempt_rows, );
try consumeUnacceptedSemanticRecoveryRetry( &runtime, alloc, attempt_flow, &attempt_rows, 8, ); try consumeUnacceptedSemanticRecoveryRetry( &runtime, alloc, attempt_flow, &attempt_rows, 2, );
var final_source = try testSource(alloc, attempt_flow, runtime.layout.cols); defer final_source.deinit(alloc); var final_prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = final_source.bytes, .owns_bytes = false, .selection = testSelectionInArea( 2, runtime.owned_top_row, runtime.layout.content_bottom, ), .cursor = .{ .cursor_row = 2, .cursor_col = 9, .replaceable_row = 2, }, }; defer final_prepared.deinit(alloc); try final_prepared.line_visual_rows.appendSlice(alloc, &attempt_rows); try makeSyntheticPreparedRenderable(&final_prepared, alloc); const final_facts = runtime.planTranscriptScroll(&final_prepared); try std.testing.expectEqual(@as(u16, 0), final_facts.resize_reflow_rows); try std.testing.expectEqual(@as(u16, 1), final_facts.semantic_rows); const final_scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, final_facts.planned_rows, ); var final_plan = testPaintPlan(&runtime, final_prepared.selection); var final_transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &final_source, &final_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(final_plan), &final_plan, final_scroll_plan, ); defer final_transition.deinit(alloc); try std.testing.expectEqual(@as(u32, 4), final_transition.visual_offset); try std.testing.expectEqual(@as(usize, 4), final_transition.selection.start_line); try std.testing.expectEqual(@as(u16, 6), final_transition.selection.last_visible_row); try std.testing.expectEqual(@as(u16, 6), final_transition.cursor_row); try std.testing.expectEqual(@as(u16, 1), final_transition.cursor_col); try std.testing.expectEqual(@as(usize, 4), final_plan.viewport.start_line); try std.testing.expectEqual(@as(u16, 6), final_plan.viewport.last_visible_row); try std.testing.expectEqual(@as(u16, 6), final_plan.cursor_target.?.row); try std.testing.expectEqual(@as(u16, 1), final_plan.cursor_target.?.col);
runtime.consumeTranscriptTransition(alloc, &final_transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = final_scroll_plan.terminal_scroll_rows, .document_append_committed = !final_transition.document_append.isEmpty(), .committed_cursor_row = runtime.layout.content_bottom, .committed_cursor_col = 1, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); const settled = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, settled.state, ); try std.testing.expectEqual(@as(u32, 4), settled.visual_offset); const projection = runtime.stableTranscriptProjectionForFlow(attempt_flow) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(@as(usize, 4), projection.selection.start_line); try std.testing.expectEqual(@as(u16, 6), projection.occupied_last_row); try std.testing.expectEqual(@as(u16, 6), projection.cursor_row); try std.testing.expectEqual(@as(u16, 1), projection.cursor_col);}
test "same-width backward footer candidate retains one coherent stable anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(120, 34, 30), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, "stable-flow", testSelection(88), 368, 19, 120, 1, );
var prepared = transcript_painter.PreparedTranscriptSurfacePaint{ .bytes = try alloc.dupe(u8, "stable-flow"), .selection = testSelection(85), .cursor = .{ .cursor_row = 30, .cursor_col = 120, .replaceable_row = 30, }, }; defer prepared.deinit(alloc); var line_rows = [_]u16{4} ** 100; for (line_rows[0..17]) |*rows| rows.* = 5; line_rows[87] = 3; try prepared.line_visual_rows.appendSlice(alloc, &line_rows);
try std.testing.expectEqual(@as(u16, 0), runtime.planTranscriptScroll(&prepared).planned_rows); var source = TranscriptPreparationSource{ .bytes = try alloc.dupe(u8, "stable-flow"), .folded_summary_indices = &.{}, .preview = .{ .natural_visual_rows = 30 }, .tail_kind = null, .tracked_entry_id = null, .tracked_entry_start_line = null, .replaceable_last_line = false, .replaceable_start = 0, .replaceable_row = 30, .welcome_cut_line = null, .welcome_boundary = null, .cols = 120, }; defer source.deinit(alloc); const layout = render_engine.frame_layout.solve(.{ .terminal = runtime.layout, .owned_top = 1, .footer = .{ .natural_rows = 4, .min_rows = 4, .max_rows = 4 }, .transcript = source.preview, .prior = runtime.committed_frame_layout, }); var plan = layout.toPaintPlan(.{ .footer_rows = .{ .top = 31, .top_divider = 31, .banner = 31, .banner_active = false, .input_base = 32, .picker_divider = 33, .picker_start = 34, .bottom_divider = 33, .hint = 34, .total_rows = 4, }, .viewport = prepared.selection, .cursor_target = .{ .row = 32, .col = 1, .visible = true }, }); const scroll_plan = render_engine.frame_scroll_plan.FrameScrollPlan.none( runtime.layout.rows, 1, ); const scroll_facts = try runtime.prepareTranscriptScrollFactsForFrame( alloc, &source, &prepared, false, false, ); const resolved = try runtime.resolveTranscriptTransitionTargetForFrame( alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromLayout(layout), scroll_plan, scroll_facts, false, false, ); try std.testing.expectEqual(@as(usize, 85), prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 30), prepared.cursor.cursor_row); try std.testing.expectEqual(@as(usize, 88), resolved.selection().start_line); try std.testing.expectEqual(@as(u16, 19), resolved.cursorRow()); try std.testing.expect(resolved.bodyDisposition() == .retain_committed); resolved.applyToPaintPlan(&plan); var transition = try runtime.sealTranscriptTransition( alloc, &source, &prepared, &plan, resolved, ); defer transition.deinit(alloc); try std.testing.expect(transition.body_disposition == .retain_committed); try std.testing.expectEqual(@as(usize, 88), transition.selection.start_line); try std.testing.expectEqual(@as(u32, 368), transition.visual_offset); try std.testing.expectEqual(@as(u16, 19), transition.cursor_row); try std.testing.expectEqual(@as(u16, 120), transition.cursor_col); try std.testing.expectEqual(@as(u16, 30), transition.materialized.cursor_row); try std.testing.expectEqual(@as(u16, 120), transition.materialized.cursor_col); try std.testing.expectEqual(@as(usize, 88), plan.viewport.start_line); try std.testing.expectEqual(@as(u16, 19), plan.cursor_target.?.row); try std.testing.expectEqual(@as(u16, 120), plan.cursor_target.?.col);
runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 0, .changed_cells = 0, .full_repaint = false, .terminal_scroll_rows_committed = 0, .document_append_committed = false, .committed_cursor_row = 19, .committed_cursor_col = 120, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), });
const diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual(transcript_runtime.TranscriptCommitDiagnosticState.stable, diagnostic.state); try std.testing.expectEqual(@as(?usize, 88), diagnostic.stable_start_line); try std.testing.expectEqual(@as(u32, 368), diagnostic.visual_offset); const stable = runtime.stableTranscriptProjectionForFlow("stable-flow").?; try std.testing.expectEqual(@as(u16, 19), stable.cursor_row); try std.testing.expectEqual(@as(u16, 120), stable.cursor_col);}
test "attempt source projections leave transcript runtime and commit state unchanged" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 12, 8), .owned_top_row = 1, .viewport_top_row = 1, .last_rendered_cols = 17, .last_paint_bottom_reserved_rows = 3, .pending_scroll_compact = true, .replaceable_last_line = true, .replaceable_row = 7, .replaceable_start = 2, }; defer runtime.deinit(alloc); var metrics: Metrics = .{}; try runtime.writeTranscript( alloc, &metrics, "alpha\nbeta\ngamma\ndelta\nepsilon\nzeta\neta\ntheta\n", true, ); const entry_id = runtime.entries.items[0].id(); try runtime.folded_command_blocks.append(alloc, .{ .summary_entry_id = entry_id, .summary_transcript_index = 777, }); runtime.paint_trace.transcript_initialized = true; runtime.paint_trace.transcript_top = 9; runtime.paint_trace.transcript_bottom = 10; runtime.paint_trace.transcript_start_line = 42; runtime.paint_trace.transcript_total_lines = 43; runtime.paint_trace.transcript_visible_rows = 2; runtime.paint_trace.transcript_partial_skip_rows = 1; runtime.paint_trace.transcript_entries = 99; runtime.paint_trace.transcript_log_frame = true; try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(2), 2, 6, 1, 9, );
const transcript_ptr = runtime.transcript.items.ptr; const transcript_capacity = runtime.transcript.capacity; const transcript_snapshot = try alloc.dupe(u8, runtime.transcript.items); defer alloc.free(transcript_snapshot); const paint_trace_snapshot = runtime.paint_trace; const commit_snapshot = runtime.transcriptCommitDiagnostic(); const cursor_row = runtime.cursor_row; const cursor_col = runtime.cursor_col; const replaceable_last_line = runtime.replaceable_last_line; const replaceable_row = runtime.replaceable_row; const replaceable_start = runtime.replaceable_start; const last_rendered_cols = runtime.last_rendered_cols; const last_paint_bottom_reserved_rows = runtime.last_paint_bottom_reserved_rows;
var source = try runtime.prepareTranscriptSource(alloc, entry_id); defer source.deinit(alloc); var collapsed = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, &source, .{ .top = 4, .bottom = 3 }, ); collapsed.deinit(alloc); var compact = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, &source, .{ .top = 1, .bottom = 2 }, ); defer compact.deinit(alloc); var expanded = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, &source, .{ .top = 1, .bottom = 8 }, ); defer expanded.deinit(alloc); _ = runtime.planTranscriptScroll(&compact); _ = runtime.planTranscriptScroll(&expanded);
try std.testing.expectEqual(transcript_ptr, runtime.transcript.items.ptr); try std.testing.expectEqual(transcript_capacity, runtime.transcript.capacity); try std.testing.expectEqualStrings(transcript_snapshot, runtime.transcript.items); try std.testing.expectEqual(@as(usize, 777), runtime.folded_command_blocks.items[0].summary_transcript_index); try std.testing.expectEqual(last_rendered_cols, runtime.last_rendered_cols); try std.testing.expectEqual(last_paint_bottom_reserved_rows, runtime.last_paint_bottom_reserved_rows); try std.testing.expect(runtime.pending_scroll_compact); try std.testing.expectEqualDeep(paint_trace_snapshot, runtime.paint_trace); try std.testing.expectEqualDeep(commit_snapshot, runtime.transcriptCommitDiagnostic()); try std.testing.expectEqual(cursor_row, runtime.cursor_row); try std.testing.expectEqual(cursor_col, runtime.cursor_col); try std.testing.expectEqual(replaceable_last_line, runtime.replaceable_last_line); try std.testing.expectEqual(replaceable_row, runtime.replaceable_row); try std.testing.expectEqual(replaceable_start, runtime.replaceable_start);}
fn checkPrepareTranscriptSourceAllocationFailures(alloc: Allocator) !void { const welcome = "fx welcome banner line one\n" ++ "fx welcome banner line two\n"; const summary = "● 2 command lines folded\n"; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 10, 6), .owned_top_row = 1, .max_transcript_bytes = 1024, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified(alloc, welcome, .welcome); const summary_entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, summary, .subagent_status, ); try runtime.folded_command_blocks.append(alloc, .{ .summary_entry_id = summary_entry_id, }); try appendFoldedLineForTest(&runtime, alloc, 0, "command output line");
var uncapped = try runtime.prepareTranscriptSource(alloc, summary_entry_id); defer uncapped.deinit(alloc); try std.testing.expectEqual(@as(usize, 4), uncapped.folded_summary_indices[0]); try std.testing.expectEqual(@as(?usize, 3), uncapped.tracked_entry_start_line); try std.testing.expectEqual(@as(u16, 6), uncapped.preview.natural_visual_rows); try std.testing.expectEqual( transcript_blocks.LeadingWelcomeBoundary{ .full_cut_line = 3, .tail_replay_line = 1, }, uncapped.welcome_boundary.?, ); try std.testing.expectEqual(@as(?usize, 3), uncapped.welcome_cut_line);
runtime.max_transcript_bytes = summary.len; var source = try runtime.prepareTranscriptSource(alloc, summary_entry_id); defer source.deinit(alloc);
try std.testing.expect(welcome.len + summary.len > runtime.max_transcript_bytes); try std.testing.expectEqualStrings(uncapped.bytes, source.bytes); try std.testing.expect(source.bytes.len > runtime.max_transcript_bytes); try std.testing.expectEqual(@as(usize, 1), source.folded_summary_indices.len); try std.testing.expectEqual(@as(usize, 4), source.folded_summary_indices[0]); try std.testing.expectEqual(@as(?usize, 3), source.tracked_entry_start_line); try std.testing.expectEqual(@as(u16, 6), source.preview.natural_visual_rows); try std.testing.expectEqual( transcript_blocks.LeadingWelcomeBoundary{ .full_cut_line = 3, .tail_replay_line = 1, }, source.welcome_boundary.?, ); try std.testing.expectEqual(@as(?usize, 3), source.welcome_cut_line);}
test "structured transcript source frees every partial allocation" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkPrepareTranscriptSourceAllocationFailures, .{}, );}
fn checkPreviewPreparationFailureLeavesRuntimeUnchanged(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 10, 6), .owned_top_row = 1, .max_transcript_bytes = 128, .last_rendered_cols = 17, }; defer runtime.deinit(alloc); try runtime.initBacking(alloc);
const entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, "recorded source\n", .subagent_status, ); runtime.transcript.clearRetainingCapacity(); try runtime.transcript.appendSlice(alloc, "poisoned cache\n"); runtime.transcript_cache_origin_untrimmed = false; try runtime.folded_command_blocks.append(alloc, .{ .summary_entry_id = entry_id, .summary_transcript_index = 777, }); runtime.transcript_band_dirty = false; runtime.paint_trace.transcript_initialized = true; runtime.paint_trace.transcript_start_line = 41;
const entry_count = runtime.entries.items.len; const next_entry_id = runtime.next_entry_id; const commit_state = runtime.transcriptCommitDiagnostic(); const paint_trace = runtime.paint_trace; const pending_repaints = runtime.render_requests.pendingReasonCount();
_ = runtime.previewTranscriptFlow(alloc) catch |err| { try std.testing.expectEqualStrings("poisoned cache\n", runtime.transcript.items); try std.testing.expectEqual(entry_count, runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings( "recorded source\n", runtime.entries.items[0].raw_bytes.bytes, ); try std.testing.expectEqual(next_entry_id, runtime.next_entry_id); try std.testing.expectEqual( @as(usize, 777), runtime.folded_command_blocks.items[0].summary_transcript_index, ); try std.testing.expectEqualDeep(commit_state, runtime.transcriptCommitDiagnostic()); try std.testing.expectEqualDeep(paint_trace, runtime.paint_trace); try std.testing.expectEqual( pending_repaints, runtime.render_requests.pendingReasonCount(), ); try std.testing.expect(!runtime.transcript_band_dirty); return err; };}
test "preview preparation failure leaves cache entries anchors and repaint state unchanged" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkPreviewPreparationFailureLeavesRuntimeUnchanged, .{}, );}
test "tail-capped raw fallback rebases retained folded indices and invalidates removed summaries" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, .full_transcript = .{ .depth = .full }, }; defer runtime.deinit(alloc);
try runtime.transcript.appendSlice(alloc, "removed-summary\nretained-summary"); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 1, }); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2, }); try appendFoldedLineForTest(&runtime, alloc, 0, "removed-output"); try appendFoldedLineForTest(&runtime, alloc, 1, "retained-output");
runtime.max_transcript_bytes = "retained-summary".len + 1; var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
try std.testing.expectEqualStrings("retained-summary", source.bytes); try std.testing.expectEqualSlices( usize, &.{ 0, 1 }, source.folded_summary_indices, );
var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), prepared.total_lines); try std.testing.expectEqual(@as(usize, 1), prepared.visible_lines.items.len); try std.testing.expect( std.meta.activeTag(prepared.visible_lines.items[0]) == .folded_line, ); try std.testing.expectEqualStrings( "retained-output", resolveVisibleLineForTest( prepared.visible_lines.items[0], .{ .bytes = prepared.bytes }, ), );
runtime.max_transcript_bytes = 4; var partial = try runtime.prepareTranscriptSource(alloc, null); defer partial.deinit(alloc); try std.testing.expectEqualStrings("mary", partial.bytes); try std.testing.expectEqualSlices( usize, &.{ 0, 0 }, partial.folded_summary_indices, );
var partial_paint = try prepareTestSourceForCurrentArea( &runtime, alloc, &partial, ); defer partial_paint.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), partial_paint.total_lines); try std.testing.expect( std.meta.activeTag(partial_paint.visible_lines.items[0]) == .transcript, ); try std.testing.expectEqualStrings( "mary", resolveVisibleLineForTest( partial_paint.visible_lines.items[0], .{ .bytes = partial_paint.bytes }, ), );}
test "structured source keeps tracked entries before the cache tail" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const removed_id = try runtime.appendRawTranscriptEntryClassified( alloc, "removed\n", .subagent_status, ); const retained_id = try runtime.appendRawTranscriptEntryClassified( alloc, "tracked\n", .subagent_status, ); runtime.max_transcript_bytes = "tracked".len + 1;
var retained = try runtime.prepareTranscriptSource(alloc, retained_id); defer retained.deinit(alloc); try std.testing.expectEqualStrings("removed\ntracked", retained.bytes); try std.testing.expectEqual(@as(?usize, 1), retained.tracked_entry_start_line);
var retained_paint = try prepareTestSourceForCurrentArea( &runtime, alloc, &retained, ); defer retained_paint.deinit(alloc); try std.testing.expectEqual(@as(?u16, 2), retained_paint.tracked_entry_row);
var removed = try runtime.prepareTranscriptSource(alloc, removed_id); defer removed.deinit(alloc); try std.testing.expectEqual(@as(?usize, 0), removed.tracked_entry_start_line);
var removed_paint = try prepareTestSourceForCurrentArea( &runtime, alloc, &removed, ); defer removed_paint.deinit(alloc); try std.testing.expectEqual(@as(?u16, 1), removed_paint.tracked_entry_row);
runtime.max_transcript_bytes = 4; var partial = try runtime.prepareTranscriptSource(alloc, retained_id); defer partial.deinit(alloc); try std.testing.expectEqualStrings("removed\ntracked", partial.bytes); try std.testing.expectEqual(@as(?usize, 1), partial.tracked_entry_start_line);
var partial_paint = try prepareTestSourceForCurrentArea( &runtime, alloc, &partial, ); defer partial_paint.deinit(alloc); try std.testing.expectEqual(@as(?u16, 2), partial_paint.tracked_entry_row);}
test "structured source keeps welcome boundary before the cache tail" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "welcome one\nwelcome two\n", .welcome, ); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "tail\n", .subagent_status, );
var uncapped = try runtime.prepareTranscriptSource(alloc, null); defer uncapped.deinit(alloc); try std.testing.expectEqual( transcript_blocks.LeadingWelcomeBoundary{ .full_cut_line = 3, .tail_replay_line = 1, }, uncapped.welcome_boundary.?, ); try std.testing.expectEqual(@as(?usize, 3), uncapped.welcome_cut_line);
runtime.max_transcript_bytes = "tail".len + 1; var capped = try runtime.prepareTranscriptSource(alloc, null); defer capped.deinit(alloc); try std.testing.expectEqualStrings("welcome one\nwelcome two\n\ntail", capped.bytes); try std.testing.expectEqual( transcript_blocks.LeadingWelcomeBoundary{ .full_cut_line = 3, .tail_replay_line = 1, }, capped.welcome_boundary.?, ); try std.testing.expectEqual(@as(?usize, 3), capped.welcome_cut_line);}
test "structured source derives replaceable span from full bytes" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 5), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "prefix\n", .subagent_status, ); _ = try runtime.appendReplaceableTranscriptLineSilentClassified( alloc, "0123456789", .subagent_status, );
runtime.max_transcript_bytes = "0123456789".len + 1; var whole_tail = try runtime.prepareTranscriptSource(alloc, null); defer whole_tail.deinit(alloc); try std.testing.expectEqualStrings("prefix\n0123456789", whole_tail.bytes); try std.testing.expect(whole_tail.replaceable_last_line); try std.testing.expectEqual(@as(usize, "prefix\n".len), whole_tail.replaceable_start);
runtime.max_transcript_bytes = 4; var partial_tail = try runtime.prepareTranscriptSource(alloc, null); defer partial_tail.deinit(alloc); try std.testing.expectEqualStrings("prefix\n0123456789", partial_tail.bytes); try std.testing.expect(partial_tail.replaceable_last_line); try std.testing.expectEqual(@as(usize, "prefix\n".len), partial_tail.replaceable_start); try std.testing.expect(partial_tail.preview.replaceable_active);
var partial_paint = try prepareTestSourceForCurrentArea( &runtime, alloc, &partial_tail, ); defer partial_paint.deinit(alloc); try std.testing.expect(partial_paint.replaceable_last_line); try std.testing.expectEqual(@as(usize, "prefix\n".len), partial_paint.replaceable_start); try std.testing.expectEqual(@as(u16, 2), partial_paint.selection.replaceable_start_row);
runtime.max_transcript_bytes = 0; var empty = try runtime.prepareTranscriptSource(alloc, null); defer empty.deinit(alloc); try std.testing.expectEqualStrings("prefix\n0123456789", empty.bytes); try std.testing.expect(empty.replaceable_last_line); try std.testing.expectEqual(@as(usize, "prefix\n".len), empty.replaceable_start); try std.testing.expectEqual(@as(u16, 2), empty.preview.natural_visual_rows); try std.testing.expectEqual(@as(u16, 0), empty.preview.trailing_boundary_blank_rows); try std.testing.expectEqual(@as(u16, 1), empty.preview.footer_boundary_gap_rows); try std.testing.expectEqual( @as(?transcript_blocks.TranscriptBlockKind, .subagent_status), empty.preview.tail_kind, ); try std.testing.expect(empty.preview.replaceable_active);}
test "structured source preview matches full paint geometry" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(4, 8, 5), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "removed-long\n", .subagent_status, ); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "tail\n\n", .subagent_status, ); runtime.max_transcript_bytes = "tail".len + 1;
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqualStrings("removed-long\ntail", source.bytes); try std.testing.expectEqual(@as(u16, 4), source.preview.natural_visual_rows); try std.testing.expect(source.preview.trailing_boundary_blank_rows >= 1); try std.testing.expect(source.preview.footer_boundary_gap_rows >= 1); try std.testing.expectEqual( @as(?transcript_blocks.TranscriptBlockKind, .subagent_status), source.preview.tail_kind, ); try std.testing.expect(!source.preview.replaceable_active);
var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try std.testing.expect(prepared.total_lines >= 1); try std.testing.expect(prepared.sourceVisualRows().len > 0);
const solved = render_engine.frame_layout.solve(.{ .terminal = runtime.layout, .owned_top = runtime.owned_top_row, .footer = .{ .natural_rows = 2, .min_rows = 2, .max_rows = 2 }, .transcript = source.preview, }); try std.testing.expect(!solved.transcript_area.isEmpty());}
test "hidden context notices do not change compact footer or activity tail policy" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); var metrics = Metrics{}; _ = try runtime.streamAssistantChunk(alloc, &metrics, "visible response"); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "context", .tone = .warning, .body = "hidden trailing context", .visibility = .full_only, }); runtime.last_paint_bottom_reserved_rows = 2;
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expect(std.mem.find(u8, source.bytes, "hidden trailing context") == null); try std.testing.expectEqual( @as(?transcript_blocks.TranscriptBlockKind, .assistant_turn), source.preview.tail_kind, ); try std.testing.expectEqual(@as(u16, 1), source.preview.footer_boundary_gap_rows); try std.testing.expectEqual(@as(u16, 1), runtime.transientAssistantGapRows()); try std.testing.expectEqual(@as(u16, 2), runtime.preservedBottomReservationRows());
var only_context = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), .owned_top_row = 1, }; defer only_context.deinit(alloc); _ = try only_context.appendSemanticNotice(alloc, .{ .topic = "context", .tone = .warning, .body = "only hidden context", .visibility = .full_only, }); only_context.last_paint_bottom_reserved_rows = 2; var empty_source = try only_context.prepareTranscriptSource(alloc, null); defer empty_source.deinit(alloc); try std.testing.expectEqualStrings("", empty_source.bytes); try std.testing.expectEqual(@as(?transcript_blocks.TranscriptBlockKind, null), empty_source.preview.tail_kind); try std.testing.expectEqual(@as(u16, 0), empty_source.preview.footer_boundary_gap_rows); try std.testing.expectEqual(@as(u16, 0), only_context.transientAssistantGapRows()); try std.testing.expectEqual(@as(u16, 0), only_context.preservedBottomReservationRows());}
test "recorded projection ignores a poisoned cache without rebuilding it" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "recorded first\n", .subagent_status, ); const tracked_id = try runtime.appendRawTranscriptEntryClassified( alloc, "recorded tracked\n", .subagent_status, ); runtime.transcript.clearRetainingCapacity(); try runtime.transcript.appendSlice(alloc, "poisoned cache\n"); runtime.transcript_cache_origin_untrimmed = false;
const preview = try runtime.previewTranscriptFlow(alloc); try std.testing.expectEqual(@as(u16, 2), preview.natural_visual_rows);
var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintForAreaTrackingEntry( alloc, &metrics, .{ .top = 1, .bottom = 4 }, tracked_id, ); defer prepared.deinit(alloc);
try std.testing.expect(std.mem.find(u8, prepared.bytes, "recorded first") != null); try std.testing.expect(std.mem.find(u8, prepared.bytes, "recorded tracked") != null); try std.testing.expect(std.mem.find(u8, prepared.bytes, "poisoned cache") == null); try std.testing.expectEqual(@as(?u16, 2), prepared.tracked_entry_row);
var shadow = try vt_emulator.Grid.init( alloc, runtime.layout.cols, runtime.layout.rows, ); defer shadow.deinit(); const plan = testPaintPlan(&runtime, prepared.selection); var surface = try render_engine.frame_surface.FrameSurface.initFromShadow( alloc, plan, shadow, ); defer surface.deinit(); _ = try runtime.paintPreparedTranscriptIntoSurface( alloc, &surface, &prepared, ); var target = try surface.copyToTargetGrid(alloc); defer target.deinit(); var row_text: std.ArrayList(u8) = .empty; defer row_text.deinit(alloc); try target.rowTextTrimmed(1, &row_text); try std.testing.expectEqualStrings("recorded first", row_text.items); row_text.clearRetainingCapacity(); try target.rowTextTrimmed(2, &row_text); try std.testing.expectEqualStrings("recorded tracked", row_text.items);
try std.testing.expectEqualStrings("poisoned cache\n", runtime.transcript.items);}
test "cache-only paint excludes record false transient bytes" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.transcript.appendSlice(alloc, "cache only\n");
var metrics: Metrics = .{}; try runtime.writeTranscript(alloc, &metrics, "transient only\n", false); try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expectEqualStrings("cache only\n", runtime.transcript.items);
const preview = try runtime.previewTranscriptFlow(alloc); try std.testing.expectEqual(@as(u16, 1), preview.natural_visual_rows); var prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = 1, .bottom = 3 }, ); defer prepared.deinit(alloc); try std.testing.expectEqualStrings("cache only\n", prepared.bytes);}
test "zero-column preparation uses cache fallback even when entries exist" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "recorded source\n", .subagent_status, ); runtime.transcript.clearRetainingCapacity(); try runtime.transcript.appendSlice(alloc, "bootstrap cache\n"); runtime.layout.cols = 0;
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqualStrings("bootstrap cache\n", source.bytes);}
test "reconstructive paint repairs a poisoned cache from entries" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "recorded reconstruction\n", .subagent_status, ); runtime.transcript.clearRetainingCapacity(); try runtime.transcript.appendSlice(alloc, "poisoned cache\n");
var metrics: Metrics = .{}; try runtime.reconstructivePaint(alloc, &metrics);
try std.testing.expect( std.mem.find(u8, runtime.transcript.items, "recorded reconstruction") != null, ); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "poisoned cache") == null); try std.testing.expect(runtime.viewport_clear_pending); try std.testing.expect(runtime.render_requests.hasReason(.transcript));}
test "append-only source remains compatible while tail replacement invalidates anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
var metrics: Metrics = .{}; try runtime.writeTranscript(alloc, &metrics, "stable", true); try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 1, 7, 19, );
try runtime.writeTranscript(alloc, &metrics, " append", true); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expect(runtime.stableTranscriptProjectionForFlow(runtime.transcript.items) != null);
const tail_id = runtime.entries.items[runtime.entries.items.len - 1].id(); try std.testing.expect(try runtime.updateRawBytesEntry( alloc, tail_id, " append extended", )); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.invalid, runtime.transcriptCommitDiagnostic().state, );}
test "prepared wrapped line delta creates visual-row inline scroll rows" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 8, .cols = 10, .content_bottom = 4, .divider_top_row = 5, .input_row = 6, .divider_bottom_row = 7, .hint_row = 8, }, .viewport_top_row = 1, .owned_top_row = 1, .cursor_row = 4, .cursor_col = 1, .has_painted_transcript = true, .has_committed_frame = true, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, "abcdefghijklmnopqrstuvwxyz\nshort\nend", testSelection(0), 0, 4, 1, 11, );
var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = 1, .bottom = 2 }, ); defer prepared.deinit(alloc);
try std.testing.expectEqual(@as(usize, 1), prepared.selection.start_line); try std.testing.expectEqual(@as(u16, 3), prepared.sourceVisualRows()[0]);
const planned = runtime.planTranscriptScroll(&prepared).planned_rows;
try std.testing.expectEqual(@as(u16, 3), planned);}
test "prepareTranscriptSurfacePaintForArea keeps rows inside solved transcript rect" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, .viewport_top_row = 1, .owned_top_row = 1, .cursor_row = 8, .cursor_col = 1, .has_painted_transcript = true, .last_paint_bottom_reserved_rows = 3, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try runtime.transcript.appendSlice(alloc, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\n");
var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = 3, .bottom = 5 }, ); defer prepared.deinit(alloc);
try std.testing.expect(prepared.selection.top_row >= 3); try std.testing.expect(prepared.selection.bottom_row <= 5); try std.testing.expect(prepared.selection.last_visible_row <= 5); try std.testing.expectEqual(@as(u16, 0), prepared.bottom_reserved_rows); try std.testing.expectEqual(@as(u16, 3), runtime.last_paint_bottom_reserved_rows);}
test "prepared transcript tracks an entry row without rerendering the selection" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, .viewport_top_row = 1, .owned_top_row = 1, .cursor_row = 8, .cursor_col = 1, .has_painted_transcript = true, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "first\n\n", .unknown_raw); const tracked_id = try runtime.appendRawTranscriptEntryClassified( alloc, "● Running command\x1b[0m\n", .tool_status, );
var metrics: Metrics = .{}; var visible = try runtime.prepareTranscriptSurfacePaintForAreaTrackingEntry( alloc, &metrics, .{ .top = 1, .bottom = 4 }, tracked_id, ); defer visible.deinit(alloc); try std.testing.expectEqual(@as(?u32, tracked_id), visible.tracked_entry_id); try std.testing.expectEqual(@as(?u16, 3), visible.tracked_entry_row);
var i: usize = 0; while (i < 6) : (i += 1) { _ = try runtime.appendRawTranscriptEntryClassified(alloc, "later\n", .unknown_raw); }
var clipped = try runtime.prepareTranscriptSurfacePaintForAreaTrackingEntry( alloc, &metrics, .{ .top = 1, .bottom = 2 }, tracked_id, ); defer clipped.deinit(alloc); try std.testing.expectEqual(@as(?u32, tracked_id), clipped.tracked_entry_id); try std.testing.expectEqual(@as(?u16, null), clipped.tracked_entry_row);}
test "prepared transcript source can omit one active entry without changing durable entries" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "before\n", .subagent_status); const active_id = try runtime.appendRawTranscriptEntryClassified(alloc, "running\n", .subagent_status); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "after\n", .subagent_status);
var active = try runtime.prepareTranscriptSourceOmittingEntry(alloc, active_id); defer active.deinit(alloc); try std.testing.expect(std.mem.find(u8, active.bytes, "running") == null); try std.testing.expect(std.mem.find(u8, active.bytes, "before") != null); try std.testing.expect(std.mem.find(u8, active.bytes, "after") != null); try std.testing.expect(!active.cache_origin_untrimmed);
var terminal = try runtime.prepareTranscriptSource(alloc, null); defer terminal.deinit(alloc); try std.testing.expect(std.mem.find(u8, terminal.bytes, "running") != null); try std.testing.expect(terminal.cache_origin_untrimmed);}
test "prepareTranscriptSurfacePaintForArea does not update committed inline scroll baseline" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 12, .cols = 40, .content_bottom = 8, .divider_top_row = 9, .input_row = 10, .divider_bottom_row = 11, .hint_row = 12, }, .viewport_top_row = 4, .owned_top_row = 4, .cursor_row = 8, .cursor_col = 1, .has_painted_transcript = true, .has_committed_frame = true, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\n", testSelection(1), 1, 8, 1, 12, ); const commit_before = runtime.transcriptCommitDiagnostic();
var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = 1, .bottom = 3 }, ); defer prepared.deinit(alloc);
try std.testing.expectEqualDeep(commit_before, runtime.transcriptCommitDiagnostic()); try std.testing.expectEqual(@as(u16, 4), runtime.owned_top_row); try std.testing.expectEqual(@as(u16, 4), runtime.viewport_top_row);}
test "full transcript paint replaces the fold summary with captured lines" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), }; defer runtime.deinit(alloc);
try runtime.transcript.appendSlice(alloc, "a\nsummary\nb\n"); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2 }); try runtime.folded_command_blocks.items[0].lines.append(alloc, .{ .stream = .stdout, .text = try alloc.dupe(u8, "hidden-1") }); try runtime.folded_command_blocks.items[0].lines.append(alloc, .{ .stream = .stderr, .text = try alloc.dupe(u8, "hidden-2") }); runtime.full_transcript.depth = .full;
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, &source, .{ .top = 1, .bottom = 8 }, ); defer prepared.deinit(alloc);
const source_buf = TranscriptBuffer{ .bytes = source.bytes }; try std.testing.expectEqual(@as(usize, 4), prepared.visible_lines.items.len); try std.testing.expectEqualStrings("a", resolveVisibleLineForTest(prepared.visible_lines.items[0], source_buf)); try std.testing.expect(std.meta.activeTag(prepared.visible_lines.items[1]) == .folded_line); try std.testing.expectEqualStrings("hidden-1", resolveVisibleLineForTest(prepared.visible_lines.items[1], source_buf)); try std.testing.expect(std.meta.activeTag(prepared.visible_lines.items[2]) == .folded_line); try std.testing.expectEqualStrings("hidden-2", resolveVisibleLineForTest(prepared.visible_lines.items[2], source_buf)); try std.testing.expectEqualStrings("b", resolveVisibleLineForTest(prepared.visible_lines.items[3], source_buf));}
test "collapsed state still shows the fold summary" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), }; defer runtime.deinit(alloc);
try runtime.transcript.appendSlice(alloc, "a\nsummary\nb\n"); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2 }); try runtime.folded_command_blocks.items[0].lines.append(alloc, .{ .stream = .stdout, .text = try alloc.dupe(u8, "hidden-1") });
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintFromSourceForArea( alloc, &metrics, &source, .{ .top = 1, .bottom = 8 }, ); defer prepared.deinit(alloc);
const source_buf = TranscriptBuffer{ .bytes = source.bytes }; try std.testing.expectEqual(@as(usize, 3), prepared.visible_lines.items.len); try std.testing.expect(std.meta.activeTag(prepared.visible_lines.items[1]) == .transcript); try std.testing.expectEqualStrings("summary", resolveVisibleLineForTest(prepared.visible_lines.items[1], source_buf));}
test "source-less surface paint keeps transcript lines one to one under full transcript" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
try runtime.transcript.appendSlice(alloc, "a\nsummary\nb\n"); try runtime.folded_command_blocks.append(alloc, .{ .summary_transcript_index = 2 }); runtime.full_transcript.depth = .full;
var metrics: Metrics = .{}; var prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = 1, .bottom = 3 }, ); defer prepared.deinit(alloc);
try std.testing.expectEqual(@as(usize, 3), prepared.visible_lines.items.len); for (prepared.visible_lines.items) |line| { try std.testing.expect(std.meta.activeTag(line) == .transcript); }}
test "record false command output remains transient" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8) }; defer runtime.deinit(alloc); var metrics = Metrics{};
try runtime.writeCommandOutputChunk( alloc, &metrics, commandOutputTestStyles(), .stdout, "transient\n", false, ); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); try std.testing.expectEqualStrings("transient", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.transcript.items.len);
try runtime.flushCommandOutputSummary( alloc, &metrics, commandOutputTestStyles(), false, ); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items.len); try std.testing.expect(runtime.command_output_display.open_command_block == null);}
test "command output consolidation preserves committed prompt scrollback anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const prompt = try alloc.dupe(u8, "give me a table of my system information"); _ = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{}, });
var live_entry_ids: [18]u32 = undefined; for (0..18) |index| { var line: [48]u8 = undefined; const text = try std.fmt.bufPrint(&line, "SYSTEM OUTPUT {d:0>2}\\n", .{index + 1}); const bytes = try alloc.dupe(u8, text); live_entry_ids[index] = try runtime.appendRawBytesEntryClassified( alloc, bytes, .unknown_raw, ); }
const first_assistant_id = try runtime.appendAssistantTurnEntry(alloc); const first_assistant = runtime.lookupAssistantSegments(first_assistant_id) orelse return error.TestExpectedAssistantSegments; for (0..24) |index| { var line: [48]u8 = undefined; const text = try std.fmt.bufPrint(&line, "assistant table row {d}\n", .{index + 1}); try first_assistant.text.appendSlice(alloc, text); } // The assistant content above is complete; report producer closure so // the finality floor does not hold the tail. runtime.transcript_release = runtime.transcript_release.with_assistant_tail_writable(false);
var live_source = try runtime.prepareTranscriptSource(alloc, null); defer live_source.deinit(alloc); try std.testing.expect(std.mem.find(u8, live_source.bytes, "give me a table") != null); try std.testing.expect(std.mem.find(u8, live_source.bytes, "SYSTEM OUTPUT 01") != null); try std.testing.expect(std.mem.find(u8, live_source.bytes, "assistant table row 24") != null); var live_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &live_source, ); defer live_prepared.deinit(alloc); try commitPreparedForTest(&runtime, alloc, &live_source, &live_prepared); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
try removeRawEntriesForTest(&runtime, alloc, &live_entry_ids); const compact_bytes = try alloc.dupe( u8, "SYSTEM OUTPUT 01\n... 16 command lines folded\nSYSTEM OUTPUT 18\n", ); _ = try runtime.appendRawBytesEntryClassified( alloc, compact_bytes, .unknown_raw, ); try runtime.rebuildTranscriptCacheAfterStructuredRewrite( alloc, "command output consolidation", ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
var metrics = Metrics{}; try runtime.writeTranscriptClassified( alloc, &metrics, "permission accepted\n", true, .subagent_status, ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
const second_assistant_id = try runtime.appendAssistantTurnEntry(alloc); const second_assistant = runtime.lookupAssistantSegments(second_assistant_id) orelse return error.TestExpectedAssistantSegments; for (0..60) |index| { var line: [40]u8 = undefined; const text = try std.fmt.bufPrint(&line, "follow-up table row {d}\n", .{index + 1}); try second_assistant.text.appendSlice(alloc, text); }
var compact_source = try runtime.prepareTranscriptSource(alloc, null); defer compact_source.deinit(alloc); try std.testing.expect(std.mem.find(u8, compact_source.bytes, "give me a table") != null); try std.testing.expect(std.mem.find(u8, compact_source.bytes, "follow-up table row 60") != null); var compact_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &compact_source, ); defer compact_prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&compact_prepared); try std.testing.expect(facts.target_visual_offset > facts.source_visual_offset); // The intervening recorded write rebases the committed prompt/assistant // prefix before the new table is admitted, so release can start here. try std.testing.expect(facts.source_compatible); try std.testing.expect(!facts.recovery_rebase); try std.testing.expect(facts.semantic_rows > 0); try std.testing.expect(facts.planned_rows > 0); try std.testing.expect(std.mem.startsWith(u8, compact_source.bytes, runtime.transcript_commit_state.stable.flow)); try std.testing.expect(std.mem.find(u8, runtime.transcript_commit_state.stable.flow, "give me a table") != null); try std.testing.expect(std.mem.find(u8, runtime.transcript_commit_state.stable.flow, "follow-up table row") == null); const history_before_release = runtime.transcriptCommitDiagnostic().history_visual_offset; try commitPreparedForTest(&runtime, alloc, &compact_source, &compact_prepared); try std.testing.expect(runtime.transcriptCommitDiagnostic().history_visual_offset > history_before_release); try std.testing.expect(runtime.transcriptCommitDiagnostic().history_visual_offset - history_before_release <= facts.semantic_rows);
// This fixture settles in that release; a repeated frame must be quiet. var settle_source = try runtime.prepareTranscriptSource(alloc, null); defer settle_source.deinit(alloc); var settle_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &settle_source, ); defer settle_prepared.deinit(alloc); const settle_facts = runtime.planTranscriptScroll(&settle_prepared); try std.testing.expect(settle_facts.source_compatible); try std.testing.expectEqual(@as(u32, 0), settle_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), settle_facts.planned_rows); const settled_history = runtime.transcriptCommitDiagnostic().history_visual_offset; try commitPreparedForTest(&runtime, alloc, &settle_source, &settle_prepared); try std.testing.expectEqual(settled_history, runtime.transcriptCommitDiagnostic().history_visual_offset); try std.testing.expectEqual(runtime.transcript_commit_state.stable.visual_offset, settled_history);}
fn removeRawEntriesForTest( runtime: *TranscriptRuntime, alloc: Allocator, ids: []const u32,) !void { for (ids) |id| { var index: usize = 0; while (index < runtime.entries.items.len) : (index += 1) { const entry = runtime.entries.items[index]; if (entry == .raw_bytes and entry.raw_bytes.id == id) { var removed = runtime.entries.orderedRemove(index); removed.deinit(alloc); break; } } else return error.TestExpectedRawEntry; }}
test "command output stores terminal-safe text for live consolidated and full transcript views" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); const raw = "\x1b[2JCSI_CLEAR_TOKEN\x1b[31mCSI_SGR_TOKEN\x1b[0m\r\x08\x07\x7f\n"; const safe = "\\x08\\x07\\x7f";
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, raw, true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stderr, "unterminated-final", true);
try std.testing.expectEqualStrings(safe, runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqualStrings("unterminated-final", runtime.command_output_blocks.items[0].lines.items[1].text);
const live = try renderEntriesToBytes(alloc, runtime.entries.items, 80); defer alloc.free(live); try std.testing.expect(std.mem.find(u8, live, safe) != null); try std.testing.expect(std.mem.find(u8, live, "\x1b[2JCSI_CLEAR_TOKEN") == null);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); const consolidated = try renderEntriesToBytes(alloc, runtime.entries.items, 80); defer alloc.free(consolidated); try std.testing.expect(std.mem.find(u8, consolidated, safe) != null); try std.testing.expect(std.mem.find(u8, consolidated, "\x1b[2JCSI_CLEAR_TOKEN") == null);
runtime.has_committed_frame = true; runtime.owned_top_row = 5; try std.testing.expect(try runtime.setTranscriptPresentationDepth(alloc, .full)); var projection = try full_transcript_screen.buildProjection( alloc, runtime.entries.items, runtime.tool_details.items, runtime.command_output_blocks.items, runtime.command_output_render.styles, runtime.layout.cols, runtime.fullTranscriptAnchorEntryId(), ); defer projection.deinit(alloc); const source_bytes = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 16, 0, null, ); var source = try runtime.prepareFullTranscriptViewportSource(alloc, source_bytes); defer source.deinit(alloc); try std.testing.expect(std.mem.find(u8, source.bytes, safe) != null); try std.testing.expect(std.mem.find(u8, source.bytes, "\x1b[2JCSI_CLEAR_TOKEN") == null);}
test "command output keeps capped and folded transcript behavior" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-one\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stderr, "line-two\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-three\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-four\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-five\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-six\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-seven\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 7), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqualStrings("line-one", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqualStrings("line-seven", runtime.command_output_blocks.items[0].lines.items[6].text);}
test "command output state keeps one authoritative folded hint" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputTestStyles();
try runtime.writeCommandOutputChunk( alloc, &metrics, styles, .stdout, "fast-1\nfast-2\nfast-3\nfast-4\nfast-5\nfast-6\n", true, ); const old_hint_entry_id = runtime.command_output_blocks.items[0].lines.items[5].entry_id.?; try expectRawEntryBytes( &runtime, old_hint_entry_id, "│ … 1 line more (ctrl+o to view)", );
try runtime.writeCommandOutputChunk( alloc, &metrics, styles, .stdout, "fast-7\nfast-8\n", true, ); const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(@as(usize, 8), block.total_lines); try std.testing.expectEqual(@as(usize, 8), block.lines.items.len); const newest_entry_id = block.lines.items[7].entry_id.?; try expectRawEntryBytes( &runtime, old_hint_entry_id, "│ … 1 line more (ctrl+o to view)", ); try expectRawEntryBytes(&runtime, newest_entry_id, "");
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), source.bytes.len);}
test "command output consolidation replaces live row before following transcript rows" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "before\n", .subagent_status); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "middle\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "after\n", .subagent_status);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
const before_pos = std.mem.indexOf(u8, source.bytes, "before") orelse return error.MissingBeforeRow; const after_pos = std.mem.indexOf(u8, source.bytes, "after") orelse return error.MissingAfterRow;
try std.testing.expect(before_pos < after_pos); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "middle") == null); try std.testing.expectEqualStrings("middle", runtime.command_output_blocks.items[0].lines.items[0].text);}
test "command output consolidation preserves rows between noncontiguous live rows" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "before\n", .subagent_status); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "command-one\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "intervening\n", .subagent_status); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "command-two\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "after\n", .subagent_status);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
const before_pos = std.mem.indexOf(u8, source.bytes, "before") orelse return error.MissingBeforeRow; const intervening_pos = std.mem.indexOf(u8, source.bytes, "intervening") orelse return error.MissingInterveningRow; const after_pos = std.mem.indexOf(u8, source.bytes, "after") orelse return error.MissingAfterRow;
try std.testing.expect(before_pos < intervening_pos); try std.testing.expect(intervening_pos < after_pos); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "command-one") == null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "command-two") == null); try std.testing.expectEqualStrings("command-one", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqualStrings("command-two", runtime.command_output_blocks.items[0].lines.items[1].text);}
test "command output folding preserves rows between noncontiguous live rows" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "before\n", .subagent_status); for (0..5) |index| { var line: [32]u8 = undefined; const text = try std.fmt.bufPrint(&line, "command-visible-{d}\n", .{index + 1}); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, text, true); } _ = try runtime.appendRawTranscriptEntryClassified(alloc, "intervening\n", .subagent_status); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "command-two\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "after\n", .subagent_status);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
const before_pos = std.mem.indexOf(u8, source.bytes, "before") orelse return error.MissingBeforeRow; const intervening_pos = std.mem.indexOf(u8, source.bytes, "intervening") orelse return error.MissingInterveningRow; const after_pos = std.mem.indexOf(u8, source.bytes, "after") orelse return error.MissingAfterRow;
try std.testing.expect(before_pos < intervening_pos); try std.testing.expect(intervening_pos < after_pos); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "command-visible-1") == null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "ctrl+o to view") == null); try std.testing.expectEqual(@as(usize, 6), runtime.command_output_blocks.items[0].lines.items.len);}
test "partial command records remain durable across an intervening notice" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); runtime.layout.cols = 16;
var metrics = Metrics{}; const styles = commandOutputTestStyles(); try runtime.writeCommandOutputChunk( alloc, &metrics, styles, .stdout, "first record wraps across many physical rows and remains intentionally long", true, ); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "intervening-notice\n", .subagent_status); try runtime.writeCommandOutputChunk( alloc, &metrics, styles, .stdout, "\nlater-hidden-record\n", true, );
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); const notice_pos = std.mem.find(u8, source.bytes, "intervening-notice") orelse return error.MissingInterveningRow; try std.testing.expect(notice_pos < source.bytes.len); try std.testing.expect(std.mem.find(u8, source.bytes, "first record") == null); try std.testing.expect(std.mem.find(u8, source.bytes, "later-hidden-record") == null); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expect(std.mem.find(u8, runtime.command_output_blocks.items[0].lines.items[0].text, "first record") != null); try std.testing.expectEqualStrings("later-hidden-record", runtime.command_output_blocks.items[0].lines.items[1].text);}
test "production command pruning preserves deferred replay around a notice" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputTestStyles();
const status_entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "● Ran printf ordered-output\n", ); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "output-0\n", true); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "intervening-notice\n", .subagent_status, ); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "output-1\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); try runtime.attachHistoricalToolDetailAfterCommandOutput( alloc, status_entry_id, .{ .id = "ordered-output", .name = "run_command", .arguments_json = "{\"command\":\"printf ordered-output\"}", }, .command, .{ .tool_call_id = @constCast("ordered-output"), .tool_name = @constCast("run_command"), .status = .success, .output = @constCast("exit_code=0\n<stdout>\noutput-0\noutput-1\n</stdout>\n"), .output_bytes = "exit_code=0\n<stdout>\noutput-0\noutput-1\n</stdout>\n".len, .stored_output_bytes = "exit_code=0\n<stdout>\noutput-0\noutput-1\n</stdout>\n".len, }, ); const pruned_source_entry_id = runtime.command_output_blocks.items[0].lines.items[1].entry_id.?;
try std.testing.expect(try command_output_runtime.trimRetainedCommandOutputLine( &runtime, alloc, 0, 1, )); _ = try command_output_runtime.syncCommandOutputBlockEntries(&runtime, alloc); try runtime.rebuildTranscriptCacheAfterStructuredRewrite(alloc, "test command prune"); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].pruned_ranges.items.len); try std.testing.expectEqual( pruned_source_entry_id, runtime.command_output_blocks.items[0].pruned_ranges.items[0].anchor_entry_id, );
var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); const compact_notice_pos = std.mem.find(u8, compact.bytes, "intervening-notice") orelse return error.MissingInterveningRow; try std.testing.expect(compact_notice_pos < compact.bytes.len); try std.testing.expect(std.mem.find(u8, compact.bytes, "output-0") == null); try std.testing.expect(std.mem.find(u8, compact.bytes, "output-1") == null); try std.testing.expect(std.mem.find(u8, compact.bytes, "ctrl+o to view") == null);
runtime.full_transcript.depth = .full; var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const source = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(source); const output_0_pos = std.mem.find(u8, source, "output-0") orelse return error.MissingFirstCommandOutputRow; const notice_pos = std.mem.find(u8, source, "intervening-notice") orelse return error.MissingInterveningRow; const output_1_pos = std.mem.find(u8, source, "output-1") orelse return error.MissingSecondCommandOutputRow; try std.testing.expect(output_0_pos < notice_pos); try std.testing.expect(notice_pos < output_1_pos);}
test "production all-pruned command keeps process and hint at final anchor" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputTestStyles();
const status_entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "● Ran printf all-pruned\n", ); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "pruned-0\n", true); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "pruned-separator\n", .subagent_status, ); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "pruned-1\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); try runtime.attachHistoricalToolDetailAfterCommandOutput( alloc, status_entry_id, .{ .id = "all-pruned", .name = "run_command", .arguments_json = "{\"command\":\"printf all-pruned\"}", }, .command, .{ .tool_call_id = @constCast("all-pruned"), .tool_name = @constCast("run_command"), .status = .success, .output = @constCast("exit_code=7\n<stdout>\npruned-0\npruned-1\n</stdout>\n"), .output_bytes = "exit_code=7\n<stdout>\npruned-0\npruned-1\n</stdout>\n".len, .stored_output_bytes = "exit_code=7\n<stdout>\npruned-0\npruned-1\n</stdout>\n".len, .command_process_presentation = .{ .exit_code = 7 }, }, ); const final_anchor = runtime.command_output_blocks.items[0].lines.items[1].entry_id.?;
try std.testing.expect(try command_output_runtime.trimRetainedCommandOutputLine( &runtime, alloc, 0, 0, )); try std.testing.expect(try command_output_runtime.trimRetainedCommandOutputLine( &runtime, alloc, 0, 0, )); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].pruned_ranges.items.len); _ = try command_output_runtime.syncCommandOutputBlockEntries(&runtime, alloc); try runtime.rebuildTranscriptCacheAfterStructuredRewrite(alloc, "test all-pruned command");
var final_anchor_bytes: ?[]const u8 = null; for (runtime.entries.items) |entry| { if (entry.id() != final_anchor or entry != .raw_bytes) continue; final_anchor_bytes = entry.raw_bytes.bytes; break; } const final = final_anchor_bytes orelse return error.MissingFinalCommandAnchor; try std.testing.expect(std.mem.find(u8, final, "│ exit code 7") != null); try std.testing.expect(std.mem.find(u8, final, "│ … 2 lines more") != null);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); const notice_pos = std.mem.find(u8, source.bytes, "pruned-separator") orelse return error.MissingInterveningRow; try std.testing.expect(notice_pos < source.bytes.len); try std.testing.expect(std.mem.find(u8, source.bytes, "│ exit code 7") == null); try std.testing.expect(std.mem.find(u8, source.bytes, "│ … 2 lines more") == null); try std.testing.expect(std.mem.find(u8, source.bytes, "pruned-0") == null); try std.testing.expect(std.mem.find(u8, source.bytes, "pruned-1") == null);}
test "pruned command ranges bridge after their separator is retained then removed" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputTestStyles();
const status_entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "● Ran printf ranges\n", ); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "range-0\nrange-1\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "range-separator\n", .subagent_status); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "range-2\nrange-3\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); try runtime.attachHistoricalToolDetailAfterCommandOutput( alloc, status_entry_id, .{ .id = "ranges", .name = "run_command", .arguments_json = "{\"command\":\"printf ranges\"}" }, .command, .{ .tool_call_id = @constCast("ranges"), .tool_name = @constCast("run_command"), .status = .success, .output = @constCast("exit_code=0\n<stdout>\nrange-0\nrange-1\nrange-2\nrange-3\n</stdout>\n"), .output_bytes = "exit_code=0\n<stdout>\nrange-0\nrange-1\nrange-2\nrange-3\n</stdout>\n".len, .stored_output_bytes = "exit_code=0\n<stdout>\nrange-0\nrange-1\nrange-2\nrange-3\n</stdout>\n".len, }, );
while (runtime.command_output_blocks.items[0].lines.items.len > 0) { try std.testing.expect(try command_output_runtime.trimRetainedCommandOutputLine( &runtime, alloc, 0, 0, )); } try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].pruned_ranges.items.len);
const retained_before = transcript_store.retainedStructuredBytes(&runtime); runtime.max_retained_transcript_bytes = retained_before - 1; try runtime.enforceStructuredRetention(alloc, null); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].pruned_ranges.items.len); const range = runtime.command_output_blocks.items[0].pruned_ranges.items[0]; try std.testing.expectEqual(@as(usize, 0), range.start_record); try std.testing.expectEqual(@as(usize, 4), range.end_record); try std.testing.expect( transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes, );}
test "command output detail retargets when first split source row is pruned" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); const lifecycle_id = types.ToolLifecycleId{ .turn_id = 3, .call_id = "split-prune" };
_ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = lifecycle_id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"printf split\"}", } }); try runtime.writeCommandOutputChunkForLifecycle(alloc, &metrics, styles, lifecycle_id, .stdout, "command-one\n", true); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "intervening\n", .subagent_status); try runtime.writeCommandOutputChunkForLifecycle(alloc, &metrics, styles, lifecycle_id, .stdout, "command-two\n", true); try runtime.flushCommandOutputSummaryForLifecycle(alloc, &metrics, styles, lifecycle_id, true); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycle_id, .outcome = .{ .kind = .completed, .summary = "Ran printf split" }, } });
const status_entry_id = runtime.toolActivityRecord(lifecycle_id).?.entry_id; const first_source_id = runtime.command_output_blocks.items[0].source_entry_ids.items[0]; const second_source_id = runtime.command_output_blocks.items[0].source_entry_ids.items[1]; try std.testing.expectEqual(first_source_id, runtime.toolDetailForEntry(status_entry_id).?.command_output_entry_id.?);
for (runtime.entries.items, 0..) |entry, entry_index| { if (entry.id() != first_source_id) continue; var removed = runtime.entries.orderedRemove(entry_index); removed.deinit(alloc); break; }
try runtime.enforceStructuredRetention(alloc, second_source_id);
try std.testing.expectEqual(second_source_id, runtime.command_output_blocks.items[0].entry_id.?); try std.testing.expectEqual(second_source_id, runtime.toolDetailForEntry(status_entry_id).?.command_output_entry_id.?); try std.testing.expectEqual(status_entry_id, runtime.detailEntryIdForCommandOutputSource(second_source_id).?);}
test "command output retains records within the compact storage limit" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "retained-one\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stderr, "retained-two\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "retained-three\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expectEqual(@as(usize, 0), runtime.folded_command_blocks.items.len); try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 3), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqualStrings("retained-one", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqualStrings("retained-two", runtime.command_output_blocks.items[0].lines.items[1].text); try std.testing.expectEqualStrings("retained-three", runtime.command_output_blocks.items[0].lines.items[2].text);}
test "command output entries are classified" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line one\n", true); try std.testing.expect(runtime.entries.items.len > 0); try std.testing.expectEqual(RawEntryClass.command_output, runtime.entries.items[runtime.entries.items.len - 1].raw_bytes.class);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expect(runtime.entries.items.len > 0); try std.testing.expectEqual(RawEntryClass.command_output, runtime.entries.items[runtime.entries.items.len - 1].raw_bytes.class);}
test "pre-flush live command output chunks match consolidated row geometry" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputAnsiTestStyles();
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "one\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stderr, "two\n", true);
const live = try renderEntriesToBytes(alloc, runtime.entries.items, 80); defer alloc.free(live);
try std.testing.expect(std.mem.find(u8, live, "\x1b[2m│ one\x1b[0m") != null); try std.testing.expect(std.mem.find(u8, live, "\x1b[2m│ two\x1b[0m") != null); try std.testing.expect(std.mem.find(u8, live, "\x1b[31m") == null);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); const consolidated = try renderEntriesToBytes(alloc, runtime.entries.items, 80); defer alloc.free(consolidated);
try std.testing.expectEqualStrings(live, consolidated);}
test "concurrent command output lifecycles remain isolated" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); const active_id = types.ToolLifecycleId{ .turn_id = 7, .call_id = "active-command" }; const foreign_id = types.ToolLifecycleId{ .turn_id = 7, .call_id = "foreign-command" };
try runtime.writeCommandOutputChunkForLifecycle(alloc, &metrics, styles, active_id, .stdout, "active-one\n", true); try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, foreign_id, .stderr, "foreign\n", true, ); try runtime.flushCommandOutputSummaryForLifecycle(alloc, &metrics, styles, foreign_id, true);
try std.testing.expect(runtime.command_output_display.open_command_block == null); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqualStrings("active-one", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[1].lines.items.len); try std.testing.expectEqualStrings("foreign", runtime.command_output_blocks.items[1].lines.items[0].text);
try runtime.writeCommandOutputChunkForLifecycle(alloc, &metrics, styles, active_id, .stderr, "active-two\n", true); try runtime.flushCommandOutputSummaryForLifecycle(alloc, &metrics, styles, active_id, true);
try std.testing.expect(runtime.command_output_display.open_command_block == null); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqual(command_output_content.Stream.stdout, runtime.command_output_blocks.items[0].lines.items[0].stream); try std.testing.expectEqual(command_output_content.Stream.stderr, runtime.command_output_blocks.items[0].lines.items[1].stream); try std.testing.expectEqualStrings("active-one", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expectEqualStrings("active-two", runtime.command_output_blocks.items[0].lines.items[1].text); try std.testing.expectEqualStrings("foreign", runtime.command_output_blocks.items[1].lines.items[0].text);}
test "command output display caps at five physical rows" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; const styles: Styles = .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "", .dim_style = "", .red_style = "", };
var line_index: usize = 0; while (line_index < 26) : (line_index += 1) { const line = try std.fmt.allocPrint(std.testing.allocator, "line-{d}\n", .{line_index}); defer std.testing.allocator.free(line); try runtime.writeCommandOutputChunk(std.testing.allocator, &metrics, styles, .stdout, line, true); } try runtime.flushCommandOutputSummary(std.testing.allocator, &metrics, styles, true);
try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 26), runtime.command_output_blocks.items[0].lines.items.len); var projection = try command_output_runtime.renderCompactCommandOutput( std.testing.allocator, runtime.command_output_blocks.items[0], .{ .styles = styles }, 80, ); defer projection.deinit(std.testing.allocator); try std.testing.expectEqual(@as(usize, 5), std.mem.count(u8, projection.bytes.items, "│ line-")); try std.testing.expect(std.mem.find(u8, projection.bytes.items, "│ line-4") != null); try std.testing.expect(std.mem.find(u8, projection.bytes.items, "│ line-5") == null); try std.testing.expect(std.mem.find(u8, projection.bytes.items, "│ … 21 lines more (ctrl+o to view)") != null);}
test "structured retention prunes old raw transcript entries past cap" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 128, .max_retained_transcript_bytes = 32, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 1); try runtime.writeTranscript(alloc, &metrics, "raw-one-12345\n", true); try runtime.writeTranscript(alloc, &metrics, "raw-two-12345\n", true); try runtime.writeTranscript(alloc, &metrics, "raw-three-123\n", true);
try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes); try std.testing.expect(runtime.entries.items.len < 3); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "raw-one") == null); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "raw-three") != null);}
test "structured retention compacts a large pruning pass in entry order" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 4096, .max_retained_transcript_bytes = std.math.maxInt(usize), }; defer runtime.deinit(alloc);
for (0..2048) |index| { const line = try std.fmt.allocPrint(alloc, "retained-entry-{d:0>4}\n", .{index}); _ = runtime.appendRawBytesEntryClassified(alloc, line, .unknown_raw) catch |err| { alloc.free(line); return err; }; }
runtime.max_retained_transcript_bytes = 1024; try runtime.enforceStructuredRetention(alloc, null);
try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= 1024); try std.testing.expect(runtime.entries.items.len < 2048); try std.testing.expect(runtime.entries.items.len > 0); for (runtime.entries.items[1..], runtime.entries.items[0 .. runtime.entries.items.len - 1]) |entry, previous| { try std.testing.expect(previous.id() < entry.id()); } try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "retained-entry-0000") == null); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "retained-entry-2047") != null);}
test "assistant streaming retention trims active segment text" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 128, .max_retained_transcript_bytes = 32, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 1); _ = try runtime.streamAssistantChunk(alloc, &metrics, "aaaaaaaaaaaaaaaa\n"); _ = try runtime.streamAssistantChunk(alloc, &metrics, "bbbbbbbbbbbbbbbb\n"); _ = try runtime.streamAssistantChunk(alloc, &metrics, "cccccccccccccccc\n");
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); const text = runtime.entries.items[0].assistant_turn.segments.text.items; try std.testing.expect(text.len <= runtime.max_retained_transcript_bytes); try std.testing.expect(std.mem.indexOf(u8, text, "cccccccc") != null); try std.testing.expect(std.mem.indexOf(u8, text, "aaaaaaaa") == null); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);}
test "hidden command output becomes count-only at the hard cap" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 512, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); const retained_lines = [_][]const u8{ "line-00\n", "line-01\n", "line-02\n", "line-03\n", "line-04\n", "line-05\n", }; for (retained_lines, 0..) |line, index| { const stream: command_output_content.Stream = if (index % 2 == 0) .stdout else .stderr; try runtime.writeCommandOutputChunk(alloc, &metrics, styles, stream, line, true); }
runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-06\n", true);
try std.testing.expectEqual(@as(usize, 6), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqualStrings("line-00", runtime.command_output_blocks.items[0].lines.items[0].text); try std.testing.expect(std.mem.find(u8, runtime.command_output_blocks.items[0].lines.items[5].text, "line-06") == null); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);
var failing = std.testing.FailingAllocator.init(alloc, .{ .fail_index = 0 }); try runtime.writeCommandOutputChunk( failing.allocator(), &metrics, styles, .stderr, "line-07\n", true, ); try std.testing.expectEqual(@as(usize, 0), failing.allocations);
try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(@as(usize, 8), block.total_lines); const expected_summary = try std.fmt.allocPrint( alloc, "│ … {d} lines more (ctrl+o to view)", .{block.total_lines - @min(@as(usize, 5), block.lines.items.len)}, ); defer alloc.free(expected_summary); var projection = try command_output_runtime.renderCompactCommandOutput( alloc, block, .{ .styles = styles }, 80, ); defer projection.deinit(alloc); try std.testing.expect(std.mem.find(u8, projection.bytes.items, expected_summary) != null); var retained_text_bytes: usize = 0; for (block.lines.items) |line| retained_text_bytes += line.text.len; try std.testing.expectEqual(retained_text_bytes, block.retained_text_bytes);
_ = try runtime.streamAssistantChunk(alloc, &metrics, "assistant after cap"); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "assistant after cap") != null);}
test "command output becomes count-only at the hard cap" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 512, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-00\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stderr, "line-01\n", true);
runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "line-02\n", true);
var failing = std.testing.FailingAllocator.init(alloc, .{ .fail_index = 0 }); try runtime.writeCommandOutputChunk( failing.allocator(), &metrics, styles, .stderr, "line-03\n", true, );
try std.testing.expectEqual(@as(usize, 0), failing.allocations); const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(@as(usize, 4), block.total_lines); try std.testing.expect(block.lines.items.len < block.total_lines); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);}
test "blank hidden command output becomes count-only at the hard cap" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 256, .max_retained_transcript_bytes = 64, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles(); for (0..8) |_| { try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "\n", true); }
const block = &runtime.command_output_blocks.items[0]; try std.testing.expect(block.lines.items.len < block.total_lines); try std.testing.expectEqual(@as(usize, 8), block.total_lines); try std.testing.expectEqual(@as(usize, 0), block.retained_text_bytes); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);
var failing = std.testing.FailingAllocator.init(alloc, .{ .fail_index = 0 }); try runtime.writeCommandOutputChunk( failing.allocator(), &metrics, styles, .stderr, "\n", true, ); try std.testing.expectEqual(@as(usize, 0), failing.allocations); try std.testing.expectEqual(@as(usize, 9), block.total_lines);}
test "command output lines count toward structured retention" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 256, .max_retained_transcript_bytes = 512, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles: Styles = .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "", .dim_style = "", .red_style = "", };
var i: usize = 0; while (i < 6) : (i += 1) { try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "hidden-command-line\n", true); } try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); try std.testing.expect(runtime.command_output_blocks.items[0].entry_id != null); try std.testing.expect(runtime.command_output_blocks.items[0].lines.items.len < 6); try std.testing.expectEqual(@as(usize, 6), runtime.command_output_blocks.items[0].total_lines); var retained_text_bytes: usize = 0; for (runtime.command_output_blocks.items[0].lines.items) |line| retained_text_bytes += line.text.len; try std.testing.expectEqual(retained_text_bytes, runtime.command_output_blocks.items[0].retained_text_bytes); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);}
test "structured retention prunes old command output block state" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 256, .max_retained_transcript_bytes = 260, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles = commandOutputTestStyles();
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "old-command-output-line-that-should-prune\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len);
try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "new-command-output-line-that-should-remain\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expect(std.mem.find(u8, runtime.command_output_blocks.items[0].lines.items[0].text, "new-command-output") != null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "old-command-output") == null); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);}
test "resize preparation uses retained entries after pruning without rewriting cache" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 128, .max_retained_transcript_bytes = 40, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 1); try runtime.writeTranscript(alloc, &metrics, "old-entry-that-will-prune\n", true); try runtime.writeTranscript(alloc, &metrics, "latest-entry-stays\n", true);
const cache_before = try alloc.dupe(u8, runtime.transcript.items); defer alloc.free(cache_before); const rendered_cols_before = runtime.last_rendered_cols;
runtime.layout.cols = 20; var prepared = try runtime.prepareTranscriptSurfacePaint(alloc, &metrics, 0); defer prepared.deinit(alloc);
try std.testing.expectEqual(rendered_cols_before, runtime.last_rendered_cols); try std.testing.expectEqualStrings(cache_before, runtime.transcript.items); try std.testing.expect(std.mem.indexOf(u8, prepared.bytes, "latest-entry-stays") != null); try std.testing.expect(std.mem.indexOf(u8, prepared.bytes, "old-entry") == null);}
test "clearTranscript after retention pruning frees retained entries and command output blocks" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22 }, .max_transcript_bytes = 128, .max_retained_transcript_bytes = 64, }; defer runtime.deinit(alloc);
var metrics = Metrics{}; const styles: Styles = .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "", .dim_style = "", .red_style = "", };
try runtime.writeTranscript(alloc, &metrics, "first-entry-to-prune\n", true); try runtime.writeTranscript(alloc, &metrics, "second-entry-to-keep\n", true); try runtime.writeCommandOutputChunk(alloc, &metrics, styles, .stdout, "hidden-command-line\n", true); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);
runtime.clearTranscript(alloc);
try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.folded_command_blocks.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 0), transcript_store.retainedStructuredBytes(&runtime));
const after = try alloc.dupe(u8, "after-clear\n"); const id = try runtime.appendRawBytesEntry(alloc, after); try std.testing.expect(id > 0);}
test "recomputeCursorFromTranscript respects resized width" { var runtime = TranscriptRuntime{ .layout = .{ .rows = 24, .cols = 6, .content_bottom = 20, .divider_top_row = 21, .input_row = 22, .divider_bottom_row = 23, .hint_row = 24, }, }; defer runtime.deinit(std.testing.allocator);
try runtime.transcript.appendSlice(std.testing.allocator, "hello world\nabc"); runtime.cursor_row = 99; runtime.cursor_col = 99;
runtime.recomputeCursorFromTranscript();
try std.testing.expectEqual(@as(u16, 3), runtime.cursor_row); try std.testing.expectEqual(@as(u16, 4), runtime.cursor_col);}
test "recomputeCursorFromTranscript skips ANSI in styled rows" { var runtime = TranscriptRuntime{ .layout = .{ .rows = 24, .cols = 80, .content_bottom = 20, .divider_top_row = 21, .input_row = 22, .divider_bottom_row = 23, .hint_row = 24, }, }; defer runtime.deinit(std.testing.allocator);
const bar_row = "\x1b[48;5;235m\x1b[39m❯ hello\x1b[0m"; try runtime.transcript.appendSlice(std.testing.allocator, bar_row);
runtime.cursor_row = 99; runtime.cursor_col = 99; runtime.recomputeCursorFromTranscript();
try std.testing.expectEqual(@as(u16, 8), runtime.cursor_col);}
test "replaceTrailingTranscriptLine updates latest replaceable line" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; try runtime.writeTranscript(std.testing.allocator, &metrics, "started\n", true); _ = try runtime.appendReplaceableTranscriptLine(std.testing.allocator, &metrics, "progress one\n"); try std.testing.expect(try runtime.replaceTrailingTranscriptLine(std.testing.allocator, &metrics, "progress two\n")); try std.testing.expectEqualStrings("started\nprogress two\n", runtime.transcript.items);}
test "replaceable line with ansi wrapper does not accumulate historical entries" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; try runtime.writeTranscript(std.testing.allocator, &metrics, "start\n", true); _ = try runtime.appendReplaceableTranscriptLine(std.testing.allocator, &metrics, "\x1b[38;5;245mline one\n\x1b[0m"); try std.testing.expect(try runtime.replaceTrailingTranscriptLine(std.testing.allocator, &metrics, "\x1b[38;5;245mline two\n\x1b[0m")); try std.testing.expectEqualStrings("start\n\x1b[38;5;245mline two\n\x1b[0m", runtime.transcript.items);}
test "updateExtraInputRows shrink preserves pre-fx scrollback" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 40, .cols = 120, .content_bottom = 36, .divider_top_row = 37, .input_row = 38, .divider_bottom_row = 39, .hint_row = 40, }, }; defer runtime.deinit(std.testing.allocator); try runtime.enableShadowVt(std.testing.allocator);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 12);
try runtime.updateExtraInputRows(std.testing.allocator, &metrics, 7); try runtime.updateExtraInputRows(std.testing.allocator, &metrics, 0);
try std.testing.expectEqual(@as(u16, 12), runtime.viewport_top_row);}
test "updateExtraInputRows after committed frame only records invalidation" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var sink = try tmp.dir.createFile(std.testing.io, "post-frame-extra-rows.log", .{ .read = true }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 20, .divider_top_row = 21, .input_row = 22, .divider_bottom_row = 23, .hint_row = 24, }, .owned_top_row = 5, .viewport_top_row = 5, .has_committed_frame = true, }; defer runtime.deinit(std.testing.allocator); runtime.footer_viewport.has_frame = true; runtime.footer_viewport.geometry = .{ .top = 21, .top_divider = 21, .input_base = 22, .bottom_divider = 23, .hint = 24, };
var metrics = Metrics{}; try runtime.updateExtraInputRows(std.testing.allocator, &metrics, 3);
try std.testing.expectEqual(@as(u16, 3), runtime.extra_input_rows); try std.testing.expect(runtime.transcript_band_dirty); try std.testing.expect(runtime.footer_viewport.externally_invalidated); try std.testing.expectEqual(@as(u64, 0), try sink.length(io_mod.getIo()));
const pending = runtime.render_requests.pendingInvalidations(); try std.testing.expectEqual(@as(u8, 1), pending.len); try std.testing.expectEqual(render_engine.paint_plan.FrameInvalidationReason.external_clear, pending.ranges()[0].reason); try std.testing.expectEqual(@as(u16, 5), pending.ranges()[0].top); try std.testing.expectEqual(@as(u16, 24), pending.ranges()[0].bottom);}
test "resize reservation never moves viewport above launch-owned row" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 46, .cols = 131, .content_bottom = 42, .divider_top_row = 43, .input_row = 44, .divider_bottom_row = 45, .hint_row = 46, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; try runtime.initViewportWithReservedRows(&metrics, 45, 11);
try std.testing.expectEqual(@as(u16, 32), runtime.viewport_top_row);
runtime.layout = .{ .rows = 44, .cols = 132, .content_bottom = 40, .divider_top_row = 41, .input_row = 42, .divider_bottom_row = 43, .hint_row = 44, }; runtime.reserveVisibleViewportRows(11);
try std.testing.expectEqual(@as(u16, 32), runtime.viewport_top_row);}
test "clearTranscript preserves launch-owned row" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 7); try runtime.writeTranscript(std.testing.allocator, &metrics, "hello\n", true);
runtime.clearTranscript(std.testing.allocator);
try std.testing.expectEqual(@as(u16, 7), runtime.owned_top_row); try std.testing.expectEqual(@as(u16, 7), runtime.viewport_top_row); try std.testing.expectEqual(@as(u16, 7), runtime.cursor_row);}
test "writeTranscript overflow defers terminal scroll to frame selection delta" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10, }, }; defer runtime.deinit(std.testing.allocator);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5); runtime.has_painted_transcript = true; try std.testing.expectEqual(@as(u16, 5), runtime.viewport_top_row);
try runtime.writeTranscript(std.testing.allocator, &metrics, "a\nb\nc\nd\ne\nf\ng\nh\n", true); try std.testing.expectEqual(@as(u16, 5), runtime.viewport_top_row); try std.testing.expectEqual(@as(u16, 5), runtime.owned_top_row); try std.testing.expectEqual(@as(u16, 7), runtime.cursor_row);}
test "appendRawBytesEntry stamps monotonic ids from 1" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const a = try alloc.dupe(u8, "first"); const id_a = try runtime.appendRawBytesEntry(alloc, a); const b = try alloc.dupe(u8, "second"); const id_b = try runtime.appendRawBytesEntry(alloc, b);
try std.testing.expectEqual(@as(u32, 1), id_a); try std.testing.expectEqual(@as(u32, 2), id_b); try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expectEqual(@as(u32, 1), runtime.entries.items[0].id()); try std.testing.expectEqualStrings("first", runtime.entries.items[0].raw_bytes.bytes);}
test "append helpers cover all three variants with unique ids" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const raw = try alloc.dupe(u8, "banner"); const raw_id = try runtime.appendRawBytesEntry(alloc, raw);
const prompt = try alloc.dupe(u8, "hello"); const user_id = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{} });
const assistant_id = try runtime.appendAssistantTurnEntry(alloc); const segs = runtime.lookupAssistantSegments(assistant_id) orelse unreachable; try segs.text.appendSlice(alloc, "streaming");
try std.testing.expectEqual(@as(u32, 1), raw_id); try std.testing.expectEqual(@as(u32, 2), user_id); try std.testing.expectEqual(@as(u32, 3), assistant_id); try std.testing.expectEqualStrings("streaming", runtime.entries.items[2].assistant_turn.segments.text.items);}
test "appendRawTranscriptEntryClassified stores raw entry class" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const entry_id = try runtime.appendRawTranscriptEntryClassified(alloc, "running tests\n", .tool_status);
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .raw_bytes); try std.testing.expectEqual(entry_id, runtime.entries.items[0].raw_bytes.id); try std.testing.expectEqual(RawEntryClass.tool_status, runtime.entries.items[0].raw_bytes.class); try std.testing.expectEqual(RawEntryClass.tool_status, runtime.rawEntryClass(entry_id).?);}
test "appendRawTranscriptEntry defaults to unknown_raw class" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const entry_id = try runtime.appendRawTranscriptEntry(alloc, "raw entry\n");
try std.testing.expectEqual(RawEntryClass.unknown_raw, runtime.rawEntryClass(entry_id).?);}
test "updateRawBytesEntry preserves raw entry class" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const entry_id = try runtime.appendRawTranscriptEntryClassified(alloc, "running\n", .tool_status); try std.testing.expect(try runtime.updateRawBytesEntry(alloc, entry_id, "done\n"));
try std.testing.expectEqual(RawEntryClass.tool_status, runtime.rawEntryClass(entry_id).?); try std.testing.expectEqualStrings("done\n", runtime.entries.items[0].raw_bytes.bytes);}
test "setRawEntryClass updates only matching raw entry" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const raw_id = try runtime.appendRawTranscriptEntry(alloc, "raw entry\n"); _ = try runtime.appendAssistantTurnEntry(alloc);
try std.testing.expect(runtime.setRawEntryClass(raw_id, .subagent_status)); try std.testing.expectEqual(RawEntryClass.subagent_status, runtime.rawEntryClass(raw_id).?); try std.testing.expect(!runtime.setRawEntryClass(999_999, .tool_status));}
test "streamAssistantChunk opens a new assistant_turn when there is no trailing one" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10 } }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5);
const id = try runtime.streamAssistantChunk(alloc, &metrics, "first chunk");
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); try std.testing.expectEqual(id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings("first chunk", runtime.entries.items[0].assistant_turn.segments.text.items); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "first chunk") != null);}
test "streamAssistantChunk extends the trailing assistant_turn on subsequent chunks" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10 } }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5);
const id_a = try runtime.streamAssistantChunk(alloc, &metrics, "alpha "); const id_b = try runtime.streamAssistantChunk(alloc, &metrics, "beta");
try std.testing.expectEqual(id_a, id_b); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); try std.testing.expectEqualStrings("alpha beta", runtime.entries.items[0].assistant_turn.segments.text.items);}
test "recorded assistant stream slow path preserves a canonical anchor when retention rewrites its source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const old_prefix = "old retained prefix\n"; _ = try runtime.appendRawTranscriptEntryClassified( alloc, old_prefix, .subagent_status, ); const long_prompt = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_prompt); var metrics: Metrics = .{}; const user_id = try runtime.writeUserPromptCard( alloc, &metrics, .{ .text = long_prompt, .images = &.{} }, false, &.{}, );
var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); // The synthetic anchor helper does not seal a source. Supply the same // entry identity as a real sealed frame before exercising retention. runtime.transcript_commit_state.stable.retention_identity = try @import("source_preparation.zig").RetentionIdentity.capture(&runtime, alloc, &committed_source); try committed_source.ensureLineIndex(alloc); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime); const user_start = for (committed_source.line_provenance, 0..) |identity, index| { if (identity == .entry and identity.entry.entry_id == user_id) break index; } else return error.TestExpectedUserEntry; const removed_rows = committed_source.transcript_visual_row_offsets[user_start]; try std.testing.expect(removed_rows > 0); const old_boundary = committed_source.byteAtVisualOffset(committed_history_visual_offset); const old_suffix = committed_source.bytes[old_boundary..]; const boundary_row = old_suffix[0..std.mem.findScalar(u8, old_suffix, '\n').?]; try std.testing.expect(std.mem.find(u8, boundary_row, "long-paste-line-34") != null);
const chunk = "trimmed assistant tail"; runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime) + chunk.len - old_prefix.len; const assistant_id = try runtime.streamAssistantChunk(alloc, &metrics, chunk);
var rebased_source = (try runtime.prepareCommittedRetentionSource(alloc)) orelse return error.TestExpectedStableTranscript; defer rebased_source.deinit(alloc); const rebased = runtime.transcript_commit_state.stable; try std.testing.expectEqual(committed_history_visual_offset - removed_rows, rebased.history_visual_offset); try std.testing.expectEqual(rebased.history_visual_offset, rebased.visual_offset); try std.testing.expectEqualStrings( committed_source.bytes[committed_source.hard_line_starts[user_start]..], rebased_source.bytes, ); try std.testing.expectEqualStrings( committed_source.bytes[old_boundary..], rebased_source.bytes[rebased_source.byteAtVisualOffset(rebased.history_visual_offset)..], ); const boundary_identity = rebased_source.line_provenance[rebased.selection.start_line]; try std.testing.expect(boundary_identity == .entry); try std.testing.expectEqual(user_id, boundary_identity.entry.entry_id); try std.testing.expectEqual(transcript_blocks.TranscriptEntryClass.user_turn, boundary_identity.entry.entry_class); try std.testing.expect(rebased.flow_materialized); try std.testing.expect(!rebased.normal_buffer_recovery_pending); try std.testing.expect(std.mem.find(u8, rebased_source.bytes, chunk) == null); try std.testing.expect( transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes, ); try std.testing.expect(runtime.lookupAssistantSegments(assistant_id) != null); var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expect(!std.mem.startsWith( u8, appended_source.bytes, committed_source.bytes, )); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, appended_source.bytes, old_prefix), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "LONG_PASTE_LAST_MARKER"), ); try std.testing.expect(runtime.entries.items[0].id() == user_id);}
const AssistantStreamFastPathCase = enum { opening, continuation,};
const AssistantStreamFastPathFailureWitness = struct { target_failures: usize = 0,};
fn checkOrdinaryAssistantStreamAllocationFailures( alloc: Allocator, test_case: AssistantStreamFastPathCase, witness: *AssistantStreamFastPathFailureWitness,) !void { const continuation = [_]u8{'b'} ** 4096; const chunk: []const u8 = switch (test_case) { .opening => "first chunk", .continuation => &continuation, };
var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8) }; defer runtime.deinit(alloc); var metrics = Metrics{};
const existing_entry_id: ?u32 = switch (test_case) { .opening => null, .continuation => try runtime.streamAssistantChunk(alloc, &metrics, "a"), }; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const assistant_before = if (existing_entry_id) |entry_id| try std.testing.allocator.dupe( u8, runtime.lookupAssistantSegments(entry_id).?.text.items, ) else null; defer if (assistant_before) |text| std.testing.allocator.free(text); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const cache_origin_before = runtime.transcript_cache_origin_untrimmed; const repaint_count_before = runtime.render_requests.pendingReasonCount();
try std.testing.expect(retained_before <= runtime.max_retained_transcript_bytes); try std.testing.expect( chunk.len <= runtime.max_retained_transcript_bytes - retained_before, ); try std.testing.expect( chunk.len > runtime.transcript.capacity - runtime.transcript.items.len, ); if (existing_entry_id) |entry_id| { const segments = runtime.lookupAssistantSegments(entry_id).?; try std.testing.expect( chunk.len > segments.text.capacity - segments.text.items.len, ); } else { try std.testing.expectEqual(@as(usize, 0), runtime.entries.capacity); }
const entry_id = runtime.streamAssistantChunk(alloc, &metrics, chunk) catch |err| { witness.target_failures += 1; try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); if (existing_entry_id) |prior_entry_id| { try std.testing.expectEqualStrings( assistant_before.?, runtime.lookupAssistantSegments(prior_entry_id).?.text.items, ); } try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(cache_origin_before, runtime.transcript_cache_origin_untrimmed); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
const opened_new_turn = test_case == .opening; const expected_entry_id = existing_entry_id orelse next_entry_id_before; try std.testing.expectEqual(expected_entry_id, entry_id); try std.testing.expectEqual( next_entry_id_before +% @intFromBool(opened_new_turn), runtime.next_entry_id, ); try std.testing.expectEqual( entry_count_before + @intFromBool(opened_new_turn), runtime.entries.items.len, ); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id());
const assistant_text = runtime.entries.items[0].assistant_turn.segments.text.items; switch (test_case) { .opening => try std.testing.expectEqualStrings(chunk, assistant_text), .continuation => { try std.testing.expectEqual(@as(usize, 1) + chunk.len, assistant_text.len); try std.testing.expectEqualStrings("a", assistant_text[0..1]); try std.testing.expectEqualStrings(chunk, assistant_text[1..]); }, } try std.testing.expectEqualStrings(assistant_text, runtime.transcript.items); try std.testing.expectEqual( assistant_text.len, transcript_store.retainedStructuredBytes(&runtime), ); try std.testing.expectEqual(repaint_count_before + 1, runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
test "ordinary assistant stream admission is atomic across allocation failures" { var opening_witness = AssistantStreamFastPathFailureWitness{}; try std.testing.checkAllAllocationFailures( std.testing.allocator, checkOrdinaryAssistantStreamAllocationFailures, .{ .opening, &opening_witness }, ); try std.testing.expectEqual(@as(usize, 3), opening_witness.target_failures);
var continuation_witness = AssistantStreamFastPathFailureWitness{}; try std.testing.checkAllAllocationFailures( std.testing.allocator, checkOrdinaryAssistantStreamAllocationFailures, .{ .continuation, &continuation_witness }, ); try std.testing.expectEqual(@as(usize, 2), continuation_witness.target_failures);}
fn checkOpeningAssistantStreamAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8) }; defer runtime.deinit(alloc); var metrics = Metrics{};
try runtime.writeTranscript(alloc, &metrics, "older transcript row\n", true); const prompt = try alloc.dupe(u8, "prompt"); _ = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{} }); runtime.max_retained_transcript_bytes = "prompt".len + "first chunk".len; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const cache_origin_before = runtime.transcript_cache_origin_untrimmed; const repaint_count_before = runtime.render_requests.pendingReasonCount();
const entry_id = runtime.streamAssistantChunk( alloc, &metrics, "first chunk", ) catch |err| { try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(cache_origin_before, runtime.transcript_cache_origin_untrimmed); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
try std.testing.expectEqual(next_entry_id_before, entry_id); try std.testing.expectEqual(next_entry_id_before +% 1, runtime.next_entry_id); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .user_turn); try std.testing.expect(runtime.entries.items[1] == .assistant_turn); try std.testing.expectEqual(entry_id, runtime.entries.items[1].id()); try std.testing.expectEqualStrings( "first chunk", runtime.entries.items[1].assistant_turn.segments.text.items, ); try std.testing.expectEqual( runtime.max_retained_transcript_bytes, transcript_store.retainedStructuredBytes(&runtime), ); const rendered = try renderEntriesToBytes( std.testing.allocator, runtime.entries.items, runtime.layout.cols, ); defer std.testing.allocator.free(rendered); try std.testing.expectEqualStrings(rendered, runtime.transcript.items); try std.testing.expectEqual(repaint_count_before + 1, runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
test "opening a recorded assistant stream is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkOpeningAssistantStreamAllocationFailures, .{}, );}
fn checkExtendingAssistantStreamAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 12, 8) }; defer runtime.deinit(alloc); var metrics = Metrics{};
try runtime.writeTranscript(alloc, &metrics, "older transcript row\n", true); const entry_id = try runtime.streamAssistantChunk(alloc, &metrics, "alpha "); runtime.max_retained_transcript_bytes = "alpha beta".len; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const assistant_before = try std.testing.allocator.dupe( u8, runtime.lookupAssistantSegments(entry_id).?.text.items, ); defer std.testing.allocator.free(assistant_before); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const cache_origin_before = runtime.transcript_cache_origin_untrimmed; const repaint_count_before = runtime.render_requests.pendingReasonCount();
const continued_id = runtime.streamAssistantChunk( alloc, &metrics, "beta", ) catch |err| { try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); try std.testing.expectEqualStrings( assistant_before, runtime.lookupAssistantSegments(entry_id).?.text.items, ); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(cache_origin_before, runtime.transcript_cache_origin_untrimmed); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
try std.testing.expectEqual(entry_id, continued_id); try std.testing.expectEqual(entry_count_before - 1, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); try std.testing.expectEqual(entry_id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings( "alpha beta", runtime.entries.items[0].assistant_turn.segments.text.items, ); try std.testing.expectEqual( runtime.max_retained_transcript_bytes, transcript_store.retainedStructuredBytes(&runtime), ); const rendered = try renderEntriesToBytes( std.testing.allocator, runtime.entries.items, runtime.layout.cols, ); defer std.testing.allocator.free(rendered); try std.testing.expectEqualStrings(rendered, runtime.transcript.items); try std.testing.expectEqual(repaint_count_before + 1, runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
test "extending a recorded assistant stream is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkExtendingAssistantStreamAllocationFailures, .{}, );}
test "paced assistant continuations do not clone retained history per chunk" { const backing = std.testing.allocator; var counted = std.testing.FailingAllocator.init(backing, .{}); const alloc = counted.allocator(); var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 24, 20), .max_transcript_bytes = 4096, }; defer runtime.deinit(alloc); try runtime.initBacking(alloc);
for (0..48) |_| { _ = try runtime.appendRawTranscriptEntryClassified( alloc, "retained transcript history row\n", .subagent_status, ); }
var metrics = Metrics{}; const entry_id = try runtime.streamAssistantChunk(alloc, &metrics, "a"); const allocations_before = counted.allocations; for (0..256) |_| { try std.testing.expectEqual( entry_id, try runtime.streamAssistantChunk(alloc, &metrics, "b"), ); }
try std.testing.expect(counted.allocations - allocations_before <= 8); try std.testing.expectEqual(@as(usize, 49), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 257), runtime.lookupAssistantSegments(entry_id).?.text.items.len);}
test "streamAssistantChunk opens a new assistant_turn after a user_turn" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10 } }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5);
_ = try runtime.streamAssistantChunk(alloc, &metrics, "assistant A"); const user_text = try alloc.dupe(u8, "next user prompt"); _ = try runtime.appendUserTurnOwned(alloc, .{ .text = user_text, .images = &.{} }); const id_after = try runtime.streamAssistantChunk(alloc, &metrics, "assistant B");
try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .assistant_turn); try std.testing.expect(runtime.entries.items[1] == .user_turn); try std.testing.expect(runtime.entries.items[2] == .assistant_turn); try std.testing.expectEqual(id_after, runtime.entries.items[2].id()); try std.testing.expectEqualStrings("assistant B", runtime.entries.items[2].assistant_turn.segments.text.items);}
test "appendUserTurnOwned frees text, images, and image slices when entries.append OOMs" { const base = std.testing.allocator; // Fail after all transferred slices exist, when the entries list first grows. var failing = std.testing.FailingAllocator.init(base, .{ .fail_index = 4 }); const alloc = failing.allocator();
var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const text = try alloc.dupe(u8, "prompt"); const images = try alloc.alloc(types.ImageAttachment, 1); images[0] = .{ .path = try alloc.dupe(u8, "/tmp/image.png"), .media_type = try alloc.dupe(u8, "image/png"), };
try std.testing.expectError( error.OutOfMemory, runtime.appendUserTurnOwned(alloc, .{ .text = text, .images = images }), );
try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expectEqual(failing.allocated_bytes, failing.freed_bytes);}
test "appendUserTurnOwnedWithSkillTokens stores selected skill metadata for reflow" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const prompt_text = "raw $review then $review"; const prompt = try alloc.dupe(u8, prompt_text); var handed_off = false; errdefer if (!handed_off) alloc.free(prompt);
const tokens = [_]input_visual_layout.SkillTokenSpan{.{ .raw_start = "raw $review then ".len, .raw_end = prompt_text.len, .name = "review", .path = "/tmp/review", }};
_ = try runtime.appendUserTurnOwnedWithSkillTokens(alloc, .{ .text = prompt, .images = &.{} }, &tokens); handed_off = true;
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items[0].user_turn.skill_tokens.len); try std.testing.expectEqualStrings("review", runtime.entries.items[0].user_turn.skill_tokens[0].name); try std.testing.expectEqual( @as(usize, prompt_text.len + "review".len + "/tmp/review".len), transcript_store.retainedStructuredBytes(&runtime), );
const rendered = try renderEntriesToBytes(alloc, runtime.entries.items, 80); defer alloc.free(rendered); try std.testing.expect(std.mem.find(u8, rendered, "$review then ") != null); try std.testing.expect(std.mem.find(u8, rendered, "\x1b[38;5;252mreview") != null); try std.testing.expect(std.mem.find(u8, rendered, "$review then $review") == null);}
const UserPromptCardAdmissionCase = enum { text_with_separator, image_with_skill_token,};
const prompt_card_skill_tokens = [_]input_visual_layout.SkillTokenSpan{.{ .raw_start = "inspect ".len, .raw_end = "inspect $review".len, .name = "review", .path = "/tmp/review",}};
fn setup_user_prompt_card_admission_runtime( runtime: *TranscriptRuntime, alloc: Allocator, test_case: UserPromptCardAdmissionCase,) !u32 { const seed_id = try runtime.appendRawTranscriptEntryClassified( alloc, "seed", .subagent_status, ); runtime.cursor_row = 2; runtime.cursor_col = switch (test_case) { .text_with_separator => 5, .image_with_skill_token => 1, }; runtime.replaceable_last_line = true; runtime.replaceable_start = 1; runtime.replaceable_row = 2; runtime.transcript_cache_origin_untrimmed = true; runtime.full_transcript.depth = .full; runtime.full_transcript.scroll_rows = 3; runtime.full_transcript.follow_tail = false; runtime.full_transcript.anchor_entry_id = seed_id; runtime.full_transcript.anchor_pending = true; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false; return seed_id;}
fn user_prompt_card_admission_input( test_case: UserPromptCardAdmissionCase, images: []types.ImageAttachment,) struct { user: types.UserTurn, skill_tokens: []const input_visual_layout.SkillTokenSpan, has_prior_turns: bool,} { return switch (test_case) { .text_with_separator => .{ .user = .{ .text = @constCast("plain prompt"), .images = &.{} }, .skill_tokens = &.{}, .has_prior_turns = false, }, .image_with_skill_token => .{ .user = .{ .text = @constCast("inspect $review"), .images = images }, .skill_tokens = &prompt_card_skill_tokens, .has_prior_turns = true, }, };}
fn check_user_prompt_card_admission_success( test_case: UserPromptCardAdmissionCase,) !void { const alloc = std.testing.allocator; const layout = transcriptTestLayout(48, 12, 8); var actual = TranscriptRuntime{ .layout = layout }; defer actual.deinit(alloc); const seed_id = try setup_user_prompt_card_admission_runtime(&actual, alloc, test_case);
var images = [_]types.ImageAttachment{.{ .id = 7, .path = @constCast("/tmp/prompt-card.png"), .media_type = @constCast("image/png"), }}; const input = user_prompt_card_admission_input(test_case, &images); const input_images_ptr = input.user.images.ptr; const card = try user_message_card.buildUserPromptCardWithSkillTokens( alloc, input.user.text, input.user.images, actual.layout.cols, input.skill_tokens, ); defer alloc.free(card); const separator = if (input.has_prior_turns) "" else "\n"; const expected_transcript = try std.fmt.allocPrint( alloc, "seed{s}{s}", .{ separator, card }, ); defer alloc.free(expected_transcript); const expected_reconstructed = try std.fmt.allocPrint( alloc, "seed\n\n{s}", .{card}, ); defer alloc.free(expected_reconstructed);
var actual_metrics = Metrics{}; const actual_id = try actual.writeUserPromptCard( alloc, &actual_metrics, input.user, input.has_prior_turns, input.skill_tokens, );
const expected_id = seed_id + @as(u32, if (input.has_prior_turns) 1 else 2); try std.testing.expectEqual(expected_id, actual_id); try std.testing.expectEqualStrings(expected_transcript, actual.transcript.items); try std.testing.expectEqual( @as(usize, if (input.has_prior_turns) 2 else 3), actual.entries.items.len, ); try std.testing.expectEqual(expected_id +% 1, actual.next_entry_id); var expected_cursor = TranscriptRuntime{ .layout = layout, .cursor_row = 2, .cursor_col = switch (test_case) { .text_with_separator => 5, .image_with_skill_token => 1, }, }; _ = expected_cursor.advanceCursor(expected_transcript["seed".len..]); try std.testing.expectEqual(expected_cursor.cursor_row, actual.cursor_row); try std.testing.expectEqual(expected_cursor.cursor_col, actual.cursor_col); try std.testing.expect(!actual.replaceable_last_line); try std.testing.expectEqual(@as(usize, 0), actual.replaceable_start); try std.testing.expectEqual(@as(u16, 2), actual.replaceable_row); try std.testing.expect(!actual.transcript_cache_origin_untrimmed); try std.testing.expect(actual.render_requests.hasReason(.transcript)); try std.testing.expectEqual(@as(usize, 1), actual.render_requests.pendingReasonCount()); try std.testing.expectEqual(@as(u32, 3), actual.full_transcript.scroll_rows); try std.testing.expect(!actual.full_transcript.follow_tail); try std.testing.expectEqual(@as(?u32, seed_id), actual.full_transcript.anchor_entry_id); try std.testing.expect(actual.full_transcript.anchor_pending);
const actual_reconstructed = try renderEntriesToBytes( alloc, actual.entries.items, actual.layout.cols, ); defer alloc.free(actual_reconstructed); try std.testing.expectEqualStrings(expected_reconstructed, actual_reconstructed); var expected_retained_bytes = "seed".len + separator.len + input.user.text.len; for (input.user.images) |image| { expected_retained_bytes += image.path.len + image.media_type.len; } for (input.skill_tokens) |token| { expected_retained_bytes += token.name.len + token.path.len; } try std.testing.expectEqual( expected_retained_bytes, transcript_store.retainedStructuredBytes(&actual), ); try std.testing.expectEqualStrings(input.user.text, actual.entries.items[actual.entries.items.len - 1].user_turn.turn.text); try std.testing.expectEqual(input_images_ptr, input.user.images.ptr);}
test "recorded user prompt card admission preserves immediate and reconstructive rendering" { try check_user_prompt_card_admission_success(.text_with_separator); try check_user_prompt_card_admission_success(.image_with_skill_token);}
test "recorded user prompt card admission preserves an authoritative committed anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 256, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 80); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, ); try std.testing.expect(!runtime.transcript_cache_origin_untrimmed);
var metrics = Metrics{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); const committed_offset = preparedVisualOffsetForTest(&committed_prepared); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, committed_offset, committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, committed_diagnostic.state, );
_ = try runtime.writeUserPromptCard( alloc, &metrics, .{ .text = @constCast("run the prepared two commands"), .images = &.{} }, true, &.{}, );
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expect(std.mem.startsWith( u8, appended_source.bytes, committed_source.bytes, )); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "LONG_PASTE_LAST_MARKER"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "run the prepared two commands"), ); const appended_flow = try alloc.dupe(u8, appended_source.bytes); defer alloc.free(appended_flow);
var appended_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer appended_prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&appended_prepared); try std.testing.expect(facts.source_compatible); try std.testing.expect(facts.semantic_rows > 0); try std.testing.expectEqual(committed_offset, facts.source_visual_offset);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, appended_prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &appended_source, &appended_prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); const settled_offset = transition.visual_offset; runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = !transition.document_append.isEmpty(), .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); const settled = runtime.transcriptCommitDiagnostic(); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, settled.state, ); try std.testing.expectEqual(settled_offset, settled.visual_offset); const projection = runtime.stableTranscriptProjectionForFlow( appended_flow, ) orelse return error.TestUnexpectedResult; try std.testing.expectEqual(settled_offset, projection.visual_offset);}
test "recorded user prompt card preserves a committed anchor after a non-prefix rewrite" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 10, 6), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
var metrics = Metrics{}; _ = try runtime.streamAssistantChunk( alloc, &metrics, "alpha beta gamma delta epsilon zeta eta theta", ); const committed_flow = try alloc.dupe(u8, runtime.transcript.items); defer alloc.free(committed_flow); try installStableAnchorForTest( &runtime, alloc, committed_flow, testSelection(0), 0, runtime.cursor_row, runtime.cursor_col, 1, );
_ = try runtime.appendSemanticNotice(alloc, .{ .topic = "response", .tone = .cancelled, .body = "Interrupted by user", }); var notice_source = try runtime.prepareTranscriptSource(alloc, null); defer notice_source.deinit(alloc); try std.testing.expect(!std.mem.startsWith( u8, notice_source.bytes, committed_flow, )); switch (runtime.transcript_commit_state) { .stable => |anchor| try std.testing.expect(anchor.normal_buffer_recovery_pending), .invalid, .recovering => return error.TestExpectedStableTranscript, }
_ = try runtime.writeUserPromptCard( alloc, &metrics, .{ .text = @constCast("queued prompt"), .images = &.{} }, true, &.{}, );
switch (runtime.transcript_commit_state) { .stable => |anchor| try std.testing.expect(anchor.normal_buffer_recovery_pending), .invalid, .recovering => return error.TestExpectedStableTranscript, } var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "queued prompt"), );}
test "recorded user prompt card preserves an anchor when retention prunes its source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "anchored transcript\n", .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime);
const prompt = "retained prompt"; runtime.max_retained_transcript_bytes = prompt.len; var metrics = Metrics{}; _ = try runtime.writeUserPromptCard( alloc, &metrics, .{ .text = @constCast(prompt), .images = &.{} }, true, &.{}, );
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, ); var retained_source = try runtime.prepareTranscriptSource(alloc, null); defer retained_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, retained_source.bytes, "anchored transcript"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, retained_source.bytes, prompt), );}
test "recorded transcript append preserves an authoritative committed anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, );
var metrics = Metrics{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); const committed_offset = preparedVisualOffsetForTest(&committed_prepared); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, committed_offset, committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
const notice = "permission accepted\ncommand starting\n"; try runtime.writeTranscriptClassified( alloc, &metrics, notice, true, .subagent_status, );
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expect(std.mem.startsWith( u8, appended_source.bytes, committed_source.bytes, )); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "LONG_PASTE_FIRST_MARKER"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "LONG_PASTE_LAST_MARKER"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "permission accepted"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "command starting"), );
var appended_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer appended_prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&appended_prepared); try std.testing.expect(facts.source_compatible); try std.testing.expect(facts.semantic_rows > 0); try std.testing.expectEqual(committed_offset, facts.source_visual_offset);}
test "theme retint preserves a capped canonical anchor and visual geometry" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, ); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "\x1b[38;5;252mRETINT_THEME_MARKER\x1b[39m\n", .subagent_status, );
var metrics: Metrics = .{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
try runtime.retintEntriesForTheme(alloc, false, true);
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); var retinted_source = try runtime.prepareTranscriptSource(alloc, null); defer retinted_source.deinit(alloc); const first_marker = std.mem.find( u8, retinted_source.bytes, "LONG_PASTE_FIRST_MARKER", ) orelse return error.TestUnexpectedResult; const last_marker = std.mem.find( u8, retinted_source.bytes, "LONG_PASTE_LAST_MARKER", ) orelse return error.TestUnexpectedResult; const theme_marker = std.mem.find( u8, retinted_source.bytes, "RETINT_THEME_MARKER", ) orelse return error.TestUnexpectedResult; try std.testing.expect(first_marker < last_marker); try std.testing.expect(last_marker < theme_marker); try std.testing.expect(std.mem.find( u8, retinted_source.bytes, "\x1b[38;5;238mRETINT_THEME_MARKER", ) != null);
var retinted_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer retinted_prepared.deinit(alloc); try std.testing.expectEqualDeep( committed_prepared.selection, retinted_prepared.selection, ); try std.testing.expectEqualSlices( u16, committed_prepared.sourceVisualRows(), retinted_prepared.sourceVisualRows(), ); try std.testing.expectEqual( committed_prepared.cursor.cursor_row, retinted_prepared.cursor.cursor_row, ); try std.testing.expectEqual( committed_prepared.cursor.cursor_col, retinted_prepared.cursor.cursor_col, );
const retinted_diagnostic = runtime.transcriptCommitDiagnostic(); const retinted_bytes = try alloc.dupe(u8, retinted_source.bytes); defer alloc.free(retinted_bytes); try runtime.retintEntriesForTheme(alloc, true, true); var unchanged_source = try runtime.prepareTranscriptSource(alloc, null); defer unchanged_source.deinit(alloc); try std.testing.expectEqualStrings(retinted_bytes, unchanged_source.bytes); try std.testing.expectEqualDeep( retinted_diagnostic, runtime.transcriptCommitDiagnostic(), );}
test "light to dark theme retint preserves retention with equal-width tokens" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "OLDER_RETINT_ENTRY\n", .subagent_status, ); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "\x1b[38;5;238mRETINT_GROWTH_MARKER\x1b[39m\n", .subagent_status, ); runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, );
try runtime.retintEntriesForTheme(alloc, true, false);
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expect( transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes, ); var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expect(std.mem.find( u8, source.bytes, "\x1b[38;5;252mRETINT_GROWTH_MARKER", ) != null); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, source.bytes, "OLDER_RETINT_ENTRY"), );}
fn checkThemeRetintAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "ALLOCATION TRANSCRIPT 01\n" ++ "ALLOCATION TRANSCRIPT 02\n" ++ "ALLOCATION TRANSCRIPT 03\n" ++ "\x1b[38;5;252mRETINT ALLOCATION MARKER\x1b[39m\n", .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, );
var source_before = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_before.deinit(std.testing.allocator); const cache_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(cache_before); const diagnostic_before = runtime.transcriptCommitDiagnostic(); const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const pending_repaints_before = runtime.render_requests.pendingReasonCount(); const cache_origin_before = runtime.transcript_cache_origin_untrimmed;
runtime.retintEntriesForTheme(alloc, false, true) catch |err| { var source_after = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_after.deinit(std.testing.allocator); try std.testing.expectEqualStrings(source_before.bytes, source_after.bytes); try std.testing.expectEqualStrings(cache_before, runtime.transcript.items); try std.testing.expectEqualDeep( diagnostic_before, runtime.transcriptCommitDiagnostic(), ); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual( pending_repaints_before, runtime.render_requests.pendingReasonCount(), ); try std.testing.expectEqual( cache_origin_before, runtime.transcript_cache_origin_untrimmed, ); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
try std.testing.expectEqualDeep( diagnostic_before, runtime.transcriptCommitDiagnostic(), ); var retinted_source = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer retinted_source.deinit(std.testing.allocator); try std.testing.expect(std.mem.find( u8, retinted_source.bytes, "\x1b[38;5;238mRETINT ALLOCATION MARKER", ) != null);}
test "theme retint is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkThemeRetintAllocationFailures, .{}, );}
test "recorded transcript append preserves an anchor when retention prunes its source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "anchored transcript\n", .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime);
const notice = "permission accepted\n"; runtime.max_retained_transcript_bytes = notice.len; var metrics = Metrics{}; try runtime.writeTranscriptClassified( alloc, &metrics, notice, true, .subagent_status, );
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, ); var retained_source = try runtime.prepareTranscriptSource(alloc, null); defer retained_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, retained_source.bytes, "anchored transcript"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, retained_source.bytes, "permission accepted"), );}
fn check_user_prompt_card_admission_allocation_failures( alloc: Allocator, test_case: UserPromptCardAdmissionCase,) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8) }; defer runtime.deinit(alloc); const seed_id = try setup_user_prompt_card_admission_runtime(&runtime, alloc, test_case);
var images = [_]types.ImageAttachment{.{ .id = 7, .path = @constCast("/tmp/prompt-card.png"), .media_type = @constCast("image/png"), }}; const input = user_prompt_card_admission_input(test_case, &images); const input_images_ptr = input.user.images.ptr; const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const reconstructed_before = try renderEntriesToBytes( std.testing.allocator, runtime.entries.items, runtime.layout.cols, ); defer std.testing.allocator.free(reconstructed_before); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const replaceable_before = runtime.replaceable_last_line; const replaceable_start_before = runtime.replaceable_start; const replaceable_row_before = runtime.replaceable_row; const cache_origin_before = runtime.transcript_cache_origin_untrimmed; const repaint_count_before = runtime.render_requests.pendingReasonCount(); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, runtime.cursor_row, runtime.cursor_col, 1, ); const commit_before = runtime.transcriptCommitDiagnostic(); const projection_before = runtime.stableTranscriptProjectionForFlow( committed_source.bytes, ) orelse return error.TestUnexpectedResult; const full_transcript_before = runtime.full_transcript; var metrics = Metrics{};
_ = runtime.writeUserPromptCard( alloc, &metrics, input.user, input.has_prior_turns, input.skill_tokens, ) catch |err| { try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(seed_id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); const reconstructed_after = try renderEntriesToBytes( std.testing.allocator, runtime.entries.items, runtime.layout.cols, ); defer std.testing.allocator.free(reconstructed_after); try std.testing.expectEqualStrings(reconstructed_before, reconstructed_after); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(replaceable_before, runtime.replaceable_last_line); try std.testing.expectEqual(replaceable_start_before, runtime.replaceable_start); try std.testing.expectEqual(replaceable_row_before, runtime.replaceable_row); try std.testing.expectEqual(cache_origin_before, runtime.transcript_cache_origin_untrimmed); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); try std.testing.expectEqualDeep(commit_before, runtime.transcriptCommitDiagnostic()); const projection_after = runtime.stableTranscriptProjectionForFlow( committed_source.bytes, ) orelse return error.TestUnexpectedResult; try std.testing.expectEqualDeep(projection_before, projection_after); try std.testing.expectEqual(full_transcript_before.scroll_rows, runtime.full_transcript.scroll_rows); try std.testing.expectEqual(full_transcript_before.follow_tail, runtime.full_transcript.follow_tail); try std.testing.expectEqual(full_transcript_before.anchor_entry_id, runtime.full_transcript.anchor_entry_id); try std.testing.expectEqual(full_transcript_before.anchor_pending, runtime.full_transcript.anchor_pending); try std.testing.expectEqualStrings(input.user.text, switch (test_case) { .text_with_separator => "plain prompt", .image_with_skill_token => "inspect $review", }); try std.testing.expectEqual(input_images_ptr, input.user.images.ptr); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expectEqual(repaint_count_before + 1, runtime.render_requests.pendingReasonCount()); try std.testing.expectEqualStrings(input.user.text, runtime.entries.items[runtime.entries.items.len - 1].user_turn.turn.text); try std.testing.expectEqual(input_images_ptr, input.user.images.ptr);}
test "recorded user prompt card admission is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, check_user_prompt_card_admission_allocation_failures, .{UserPromptCardAdmissionCase.text_with_separator}, ); try std.testing.checkAllAllocationFailures( std.testing.allocator, check_user_prompt_card_admission_allocation_failures, .{UserPromptCardAdmissionCase.image_with_skill_token}, );}
test "tailAssistantSegments returns trailing assistant or null" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
try std.testing.expect(runtime.tailAssistantSegments() == null);
const bytes = try alloc.dupe(u8, "banner"); _ = try runtime.appendRawBytesEntry(alloc, bytes); try std.testing.expect(runtime.tailAssistantSegments() == null);
const assistant_id = try runtime.appendAssistantTurnEntry(alloc); const segs = runtime.lookupAssistantSegments(assistant_id) orelse unreachable; try segs.text.appendSlice(alloc, "tok1"); const tail = runtime.tailAssistantSegments(); try std.testing.expect(tail != null); try std.testing.expectEqualStrings("tok1", tail.?.text.items);
const trailing = try alloc.dupe(u8, "later"); _ = try runtime.appendRawBytesEntry(alloc, trailing); try std.testing.expect(runtime.tailAssistantSegments() == null);}
test "lookupAssistantSegments resolves assistant ids and rejects others" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const prompt = try alloc.dupe(u8, "hello"); const user_id = try runtime.appendUserTurnOwned(alloc, .{ .text = prompt, .images = &.{} });
const assistant_id = try runtime.appendAssistantTurnEntry(alloc); const segs = runtime.lookupAssistantSegments(assistant_id) orelse unreachable; try segs.text.appendSlice(alloc, "tok");
const looked_up = runtime.lookupAssistantSegments(assistant_id); try std.testing.expect(looked_up != null); try std.testing.expectEqualStrings("tok", looked_up.?.text.items);
const raw = try alloc.dupe(u8, "banner"); const raw_id = try runtime.appendRawBytesEntry(alloc, raw); try std.testing.expect(runtime.lookupAssistantSegments(raw_id) == null);
try std.testing.expect(runtime.lookupAssistantSegments(user_id) == null);
try std.testing.expect(runtime.lookupAssistantSegments(std.math.maxInt(u32)) == null);}
test "clearTranscript drops entries but preserves next_entry_id" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const first = try alloc.dupe(u8, "first"); _ = try runtime.appendRawBytesEntry(alloc, first); const second = try alloc.dupe(u8, "second"); _ = try runtime.appendRawBytesEntry(alloc, second);
runtime.clearTranscript(alloc); try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len);
const after = try alloc.dupe(u8, "after-clear"); const id_after = try runtime.appendRawBytesEntry(alloc, after); try std.testing.expectEqual(@as(u32, 3), id_after);}
test "writeTranscript auto-mirrors a raw_bytes entry" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10 } }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5); try runtime.writeTranscript(alloc, &metrics, "banner line\n", true);
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .raw_bytes); try std.testing.expectEqualStrings("banner line\n", runtime.entries.items[0].raw_bytes.bytes);}
test "writeTranscriptClassified records raw entry class" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc); var metrics = Metrics{};
try runtime.writeTranscriptClassified(alloc, &metrics, "cancelled\n", true, .question_resolution);
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(RawEntryClass.question_resolution, runtime.entries.items[0].raw_bytes.class);}
const RecordedWriteToolDetailFixture = struct { entry_id: u32, tool_name: []const u8, arguments_json: []const u8, result: []const u8, result_handle: []const u8, turn_id: u64, call_id: []const u8, command_output_entry_id: u32,};
fn appendRecordedWriteToolDetailFixture( runtime: *TranscriptRuntime, alloc: Allocator, fixture: RecordedWriteToolDetailFixture,) !void { var detail: ToolDetailRecord = .{ .entry_id = fixture.entry_id, .tool_name = try alloc.dupe(u8, fixture.tool_name), }; errdefer detail.deinit(alloc); detail.arguments_json = try alloc.dupe(u8, fixture.arguments_json); detail.result = try alloc.dupe(u8, fixture.result); detail.result_handle = try alloc.dupe(u8, fixture.result_handle); detail.outcome = .completed; const lifecycle_call_id = try alloc.dupe(u8, fixture.call_id); detail.lifecycle_id = .{ .turn_id = fixture.turn_id, .call_id = lifecycle_call_id, }; detail.command_output_entry_id = fixture.command_output_entry_id; try runtime.tool_details.append(alloc, detail);}
fn expectRecordedWriteToolDetailFixture( runtime: *const TranscriptRuntime, fixture: RecordedWriteToolDetailFixture,) !void { const detail = runtime.toolDetailForEntry(fixture.entry_id); try std.testing.expect(detail != null); try std.testing.expectEqualStrings(fixture.tool_name, detail.?.tool_name); try std.testing.expectEqualStrings(fixture.arguments_json, detail.?.arguments_json.?); try std.testing.expectEqualStrings(fixture.result, detail.?.result.?); try std.testing.expectEqualStrings(fixture.result_handle, detail.?.result_handle.?); try std.testing.expectEqual(types.ToolOutcomeKind.completed, detail.?.outcome.?); try std.testing.expectEqual(fixture.turn_id, detail.?.lifecycle_id.?.turn_id); try std.testing.expectEqualStrings(fixture.call_id, detail.?.lifecycle_id.?.call_id); try std.testing.expectEqual(fixture.command_output_entry_id, detail.?.command_output_entry_id.?);}
fn appendOwnedToolDetail( runtime: *TranscriptRuntime, alloc: Allocator, entry_id: u32, tool_name: []const u8,) !void { var detail: ToolDetailRecord = .{ .entry_id = entry_id, .tool_name = try alloc.dupe(u8, tool_name), }; errdefer detail.deinit(alloc); try runtime.tool_details.append(alloc, detail);}
test "historical tool detail insertion preserves entry id order" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
for ([_]u32{ 30, 10, 20 }) |entry_id| { try runtime.attachHistoricalToolDetail( alloc, entry_id, .{ .id = "call", .name = "read_file", .arguments_json = "{}", }, .read, .{ .tool_call_id = @constCast("call"), .tool_name = @constCast("read_file"), .status = .success, .output = @constCast("result"), .output_bytes = 6, .stored_output_bytes = 6, }, ); }
try std.testing.expectEqual(@as(usize, 3), runtime.tool_details.items.len); try std.testing.expectEqual(@as(u32, 10), runtime.tool_details.items[0].entry_id); try std.testing.expectEqual(@as(u32, 20), runtime.tool_details.items[1].entry_id); try std.testing.expectEqual(@as(u32, 30), runtime.tool_details.items[2].entry_id); try std.testing.expect(runtime.toolDetailForEntry(9) == null); try std.testing.expectEqual(@as(u32, 10), runtime.toolDetailForEntry(10).?.entry_id); try std.testing.expectEqual(@as(u32, 20), runtime.toolDetailForEntry(20).?.entry_id); try std.testing.expectEqual(@as(u32, 30), runtime.toolDetailForEntry(30).?.entry_id); try std.testing.expect(runtime.toolDetailForEntry(31) == null);
try runtime.attachHistoricalToolDetail( alloc, 20, .{ .id = "updated-call", .name = "glob_files", .arguments_json = "{\"path\":\"src\"}", }, .list, .{ .tool_call_id = @constCast("updated-call"), .tool_name = @constCast("glob_files"), .status = .success, .output = @constCast("updated result"), .output_bytes = 14, .stored_output_bytes = 14, }, ); try std.testing.expectEqual(@as(usize, 3), runtime.tool_details.items.len); const updated = runtime.toolDetailForEntry(20).?; try std.testing.expectEqualStrings("glob_files", updated.tool_name); try std.testing.expectEqualStrings("{\"path\":\"src\"}", updated.arguments_json.?); try std.testing.expectEqualStrings("updated result", updated.result.?);}
test "tool detail pruning preserves retained payload order and capacity" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
try appendOwnedToolDetail(&runtime, alloc, 1, "first"); try appendOwnedToolDetail(&runtime, alloc, 2, "second"); try appendOwnedToolDetail(&runtime, alloc, 3, "third"); try appendOwnedToolDetail(&runtime, alloc, 4, "fourth"); try appendOwnedToolDetail(&runtime, alloc, 5, "fifth"); const initial_capacity = runtime.tool_details.capacity;
var retained_entry_ids: transcript_store.EntryIdSet = .empty; defer retained_entry_ids.deinit(alloc); try retained_entry_ids.put(alloc, 2, {}); try retained_entry_ids.put(alloc, 4, {});
runtime.pruneToolDetailsForRetainedEntries(alloc, &retained_entry_ids);
try std.testing.expectEqual(@as(usize, 2), runtime.tool_details.items.len); try std.testing.expectEqual(initial_capacity, runtime.tool_details.capacity); try std.testing.expectEqual(@as(u32, 2), runtime.tool_details.items[0].entry_id); try std.testing.expectEqualStrings("second", runtime.tool_details.items[0].tool_name); try std.testing.expectEqual(@as(u32, 4), runtime.tool_details.items[1].entry_id); try std.testing.expectEqualStrings("fourth", runtime.tool_details.items[1].tool_name);}
test "tool detail pruning stays bounded at fifty thousand records" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const detail_count = 50_000; try runtime.tool_details.ensureTotalCapacity(alloc, detail_count);
var retained_entry_ids: transcript_store.EntryIdSet = .empty; defer retained_entry_ids.deinit(alloc); try retained_entry_ids.ensureTotalCapacity(alloc, detail_count / 2);
for (0..detail_count) |index| { const entry_id: u32 = @intCast(index + 1); try appendOwnedToolDetail(&runtime, alloc, entry_id, "read_file"); if (index % 2 == 0) { retained_entry_ids.putAssumeCapacity(entry_id, {}); } }
const lookup_started = std.Io.Clock.Timestamp.now(io_mod.getIo(), .awake); for (0..detail_count) |index| { const entry_id: u32 = @intCast(index + 1); try std.testing.expect(runtime.toolDetailForEntry(entry_id) != null); } try std.testing.expect(runtime.toolDetailForEntry( @as(u32, @intCast(detail_count + 1)), ) == null); const lookup_elapsed_ms = lookup_started.durationTo( std.Io.Clock.Timestamp.now(io_mod.getIo(), .awake), ).raw.toMilliseconds(); try std.testing.expect(lookup_elapsed_ms < 1_000);
const started = std.Io.Clock.Timestamp.now(io_mod.getIo(), .awake); runtime.pruneToolDetailsForRetainedEntries(alloc, &retained_entry_ids); const elapsed_ms = started.durationTo( std.Io.Clock.Timestamp.now(io_mod.getIo(), .awake), ).raw.toMilliseconds();
try std.testing.expect(elapsed_ms < 1_000); try std.testing.expectEqual(detail_count / 2, runtime.tool_details.items.len); for (runtime.tool_details.items, 0..) |detail, index| { try std.testing.expectEqual(@as(u32, @intCast(index * 2 + 1)), detail.entry_id); try std.testing.expectEqualStrings("read_file", detail.tool_name); }}
const RecordedWriteCommandOutputFixture = struct { entry_id: u32, turn_id: u64, call_id: []const u8, line: []const u8,};
fn appendRecordedWriteCommandOutputFixture( runtime: *TranscriptRuntime, alloc: Allocator, fixture: RecordedWriteCommandOutputFixture,) !void { const Block = @TypeOf(runtime.command_output_blocks.items[0]); var block: Block = .{ .entry_id = fixture.entry_id, .lifecycle_id = .{ .turn_id = fixture.turn_id, .call_id = try alloc.dupe(u8, fixture.call_id), }, }; errdefer block.deinit(alloc); const line = try alloc.dupe(u8, fixture.line); block.lines.append(alloc, .{ .stream = .stdout, .text = line, }) catch |err| { alloc.free(line); return err; }; block.total_lines = 1; block.retained_text_bytes = line.len; try runtime.command_output_blocks.append(alloc, block);}
fn expectRecordedWriteCommandOutputFixture( runtime: *const TranscriptRuntime, fixture: RecordedWriteCommandOutputFixture,) !void { try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(fixture.entry_id, block.entry_id.?); try std.testing.expect(block.lifecycle_id != null); try std.testing.expectEqual(fixture.turn_id, block.lifecycle_id.?.turn_id); try std.testing.expectEqualStrings(fixture.call_id, block.lifecycle_id.?.call_id); try std.testing.expectEqual(@as(usize, 1), block.lines.items.len); try std.testing.expectEqual(command_output_content.Stream.stdout, block.lines.items[0].stream); try std.testing.expectEqualStrings(fixture.line, block.lines.items[0].text);}
test "command output retention preserves its artifact detail owner" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .max_retained_transcript_bytes = 128 }; defer runtime.deinit(alloc);
const detail_entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, "● Ran command\n", .tool_status, ); const command_entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, "│ ... command lines folded\n", .command_output, ); const detail: RecordedWriteToolDetailFixture = .{ .entry_id = detail_entry_id, .tool_name = "run_command", .arguments_json = "{\"command\":\"generate output\"}", .result = "output_file=/tmp/fx-command-retained.log\n", .result_handle = "result-run-command.txt", .turn_id = 1, .call_id = "retained-command", .command_output_entry_id = command_entry_id, }; try appendRecordedWriteToolDetailFixture(&runtime, alloc, detail); try appendRecordedWriteCommandOutputFixture(&runtime, alloc, .{ .entry_id = command_entry_id, .turn_id = 1, .call_id = "retained-command", .line = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", }); runtime.command_output_blocks.items[0].total_lines = 2;
try transcript_store.enforceStructuredRetention(&runtime, alloc, command_entry_id);
try std.testing.expect(runtime.toolDetailForEntry(detail_entry_id) != null); try std.testing.expect(runtime.rawEntryClass(detail_entry_id) == .tool_status); try std.testing.expect(runtime.rawEntryClass(command_entry_id) == .command_output); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].total_lines); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items[0].retained_text_bytes); try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes);}
fn checkRecordedTranscriptWriteAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, .viewport_top_row = 1, .cursor_row = 2, .cursor_col = 4, .max_transcript_bytes = 8, .max_retained_transcript_bytes = 19 + @sizeOf(command_output_runtime.CommandOutputLine), .command_output_render = .{}, }; defer runtime.deinit(alloc);
const existing_id = try runtime.appendRawTranscriptEntryClassified( alloc, "old\n", .subagent_status, ); const replaceable_id = try runtime.appendRawTranscriptEntryClassified( alloc, "tail\n", .subagent_status, ); const pruned_detail: RecordedWriteToolDetailFixture = .{ .entry_id = existing_id, .tool_name = "read_file", .arguments_json = "{\"path\":\"old\"}", .result = "old result", .result_handle = "old-result.txt", .turn_id = 1, .call_id = "call-old", .command_output_entry_id = existing_id, }; const retained_detail: RecordedWriteToolDetailFixture = .{ .entry_id = replaceable_id, .tool_name = "write_file", .arguments_json = "{\"path\":\"tail\"}", .result = "tail result", .result_handle = "tail-result.txt", .turn_id = 2, .call_id = "call-tail", .command_output_entry_id = replaceable_id, }; const retained_command_output: RecordedWriteCommandOutputFixture = .{ .entry_id = replaceable_id, .turn_id = 3, .call_id = "call-command-tail", .line = "x", }; try appendRecordedWriteToolDetailFixture(&runtime, alloc, pruned_detail); try appendRecordedWriteToolDetailFixture(&runtime, alloc, retained_detail); try appendRecordedWriteCommandOutputFixture(&runtime, alloc, retained_command_output); runtime.cursor_row = 2; runtime.cursor_col = 4; runtime.replaceable_last_line = true; runtime.replaceable_start = 0; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const replaceable_before = runtime.replaceable_last_line; const replaceable_start_before = runtime.replaceable_start; const repaint_count_before = runtime.render_requests.pendingReasonCount(); const transcript_before = "tail\n"; try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items);
var metrics = Metrics{}; const entry_id = transcript_writer.writeTranscriptReturningEntryIdClassified( &runtime, alloc, &metrics, "first\nsecond\n", true, .question_resolution, ) catch |err| { try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(existing_id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings("old\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expectEqual(replaceable_id, runtime.entries.items[1].id()); try std.testing.expectEqualStrings("tail\n", runtime.entries.items[1].raw_bytes.bytes); try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(replaceable_before, runtime.replaceable_last_line); try std.testing.expectEqual(replaceable_start_before, runtime.replaceable_start); try std.testing.expectEqual(@as(usize, 2), runtime.tool_details.items.len); try expectRecordedWriteToolDetailFixture(&runtime, pruned_detail); try expectRecordedWriteToolDetailFixture(&runtime, retained_detail); try expectRecordedWriteCommandOutputFixture(&runtime, retained_command_output); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return err; };
try std.testing.expectEqual(@as(u32, 3), entry_id); try std.testing.expectEqual(@as(u32, 4), runtime.next_entry_id); try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expectEqual(replaceable_id, runtime.entries.items[0].id()); try std.testing.expectEqual(entry_id, runtime.entries.items[1].id()); try std.testing.expectEqual(RawEntryClass.question_resolution, runtime.entries.items[1].raw_bytes.class); try std.testing.expectEqualStrings("first\nsecond\n", runtime.entries.items[1].raw_bytes.bytes); try std.testing.expectEqualStrings("second\n", runtime.transcript.items); try std.testing.expect(!runtime.replaceable_last_line); try std.testing.expectEqual(@as(usize, 0), runtime.replaceable_start); try std.testing.expectEqual(@as(usize, 1), runtime.tool_details.items.len); try std.testing.expect(runtime.toolDetailForEntry(existing_id) == null); try expectRecordedWriteToolDetailFixture(&runtime, retained_detail); try expectRecordedWriteCommandOutputFixture(&runtime, retained_command_output); try std.testing.expectEqual(@as(usize, 1), runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
test "recorded transcript write is atomic across entry cache and retention allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkRecordedTranscriptWriteAllocationFailures, .{}, );}
test "context notices stay canonical and ordered across compact and full projections" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 24, 20) }; defer runtime.deinit(alloc); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "context", .tone = .warning, .body = "context-first", .visibility = .full_only, }); try std.testing.expectEqualStrings("", runtime.transcript.items); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "system", .tone = .information, .body = "ordinary-system", }); const compact_before_open = try alloc.dupe(u8, runtime.transcript.items); defer alloc.free(compact_before_open); try std.testing.expect(std.mem.find(u8, compact_before_open, "context-first") == null); try std.testing.expect(std.mem.find(u8, compact_before_open, "ordinary-system") != null);
try std.testing.expect(try runtime.setTranscriptPresentationDepth(alloc, .full)); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "context", .tone = .warning, .body = "context-second", .visibility = .full_only, }); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "system", .tone = .@"error", .body = "ordinary-error", }); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "context-first") == null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "context-second") == null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "ordinary-system") != null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "ordinary-error") != null); const compact_while_open = try alloc.dupe(u8, runtime.transcript.items); defer alloc.free(compact_while_open);
try std.testing.expectEqual(@as(usize, 4), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .semantic_notice); try std.testing.expectEqual(types.NoticeVisibility.full_only, runtime.entries.items[0].semantic_notice.visibility); try std.testing.expectEqualStrings("context-first", runtime.entries.items[0].semantic_notice.body); try std.testing.expect(runtime.entries.items[1] == .semantic_notice); try std.testing.expectEqual(types.NoticeVisibility.compact_and_full, runtime.entries.items[1].semantic_notice.visibility); try std.testing.expectEqualStrings("ordinary-system", runtime.entries.items[1].semantic_notice.body); try std.testing.expect(runtime.entries.items[2] == .semantic_notice); try std.testing.expectEqual(types.NoticeVisibility.full_only, runtime.entries.items[2].semantic_notice.visibility); try std.testing.expectEqualStrings("context-second", runtime.entries.items[2].semantic_notice.body); try std.testing.expect(runtime.entries.items[3] == .semantic_notice); try std.testing.expectEqual(types.NoticeTone.@"error", runtime.entries.items[3].semantic_notice.tone);
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); const first_pos = std.mem.find(u8, full, "context-first") orelse return error.MissingFirstContextNotice; const system_pos = std.mem.find(u8, full, "ordinary-system") orelse return error.MissingOrdinarySystemNotice; const second_pos = std.mem.find(u8, full, "context-second") orelse return error.MissingSecondContextNotice; const error_pos = std.mem.find(u8, full, "ordinary-error") orelse return error.MissingOrdinaryErrorNotice; try std.testing.expect(first_pos < system_pos); try std.testing.expect(system_pos < second_pos); try std.testing.expect(second_pos < error_pos);
try std.testing.expect(try runtime.setTranscriptPresentationDepth(alloc, .inline_mode)); try std.testing.expect(!runtime.fullTranscriptActive()); try std.testing.expectEqualStrings(compact_while_open, runtime.transcript.items); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "context-first") == null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "context-second") == null);}
fn checkContextNoticeAllocationFailures(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 12, 8) }; defer runtime.deinit(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "visible-before\n", .subagent_status, ); runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false;
const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; _ = transcript_store.appendSemanticNoticeAtomic( &runtime, alloc, .{ .topic = "context", .tone = .warning, .body = "hidden-context", .visibility = .full_only, }, ) catch |err| { try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(RawEntryClass.subagent_status, runtime.entries.items[0].raw_bytes.class); try std.testing.expectEqualStrings("visible-before\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expectEqualStrings("visible-before\n", runtime.transcript.items); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return err; };
try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[1] == .semantic_notice); try std.testing.expectEqualStrings("context", runtime.entries.items[1].semantic_notice.topic); try std.testing.expectEqualStrings("hidden-context", runtime.entries.items[1].semantic_notice.body); try std.testing.expectEqual(types.NoticeVisibility.full_only, runtime.entries.items[1].semantic_notice.visibility); try std.testing.expectEqualStrings("visible-before\n", runtime.transcript.items); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
test "context notice admission is atomic across entry cache and retention allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkContextNoticeAllocationFailures, .{}, );}
test "repeated recorded mutations keep tool detail capacity bounded" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8) }; defer runtime.deinit(alloc);
const fixture: RecordedWriteToolDetailFixture = .{ .entry_id = 1, .tool_name = "run_command", .arguments_json = "{\"command\":\"pwd\"}", .result = "workspace", .result_handle = "command-output.txt", .turn_id = 1, .call_id = "bounded-tool-detail", .command_output_entry_id = 1, }; try appendRecordedWriteToolDetailFixture(&runtime, alloc, fixture); const initial_capacity = runtime.tool_details.capacity; var metrics = Metrics{};
for (0..4) |_| { try runtime.writeTranscriptClassified( alloc, &metrics, "recorded\n", true, .subagent_status, ); }
try std.testing.expect(runtime.tool_details.capacity <= initial_capacity); try expectRecordedWriteToolDetailFixture(&runtime, fixture);}
const RecordedCommandOutputAtomicFixture = struct { lifecycle_id: types.ToolLifecycleId, status_entry_id: u32,};
fn setupRecordedCommandOutputAtomicFixture( runtime: *TranscriptRuntime, alloc: Allocator,) !RecordedCommandOutputAtomicFixture { const lifecycle_id = types.ToolLifecycleId{ .turn_id = 71, .call_id = "atomic-command-output", }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = lifecycle_id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"printf atomic\"}", } }); const status_entry_id = runtime.toolActivityRecord(lifecycle_id).?.entry_id; runtime.full_transcript.depth = .full; runtime.full_transcript.scroll_rows = 3; runtime.full_transcript.follow_tail = false; runtime.full_transcript.anchor_entry_id = status_entry_id; runtime.full_transcript.anchor_pending = true; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false; return .{ .lifecycle_id = lifecycle_id, .status_entry_id = status_entry_id, };}
fn expectRecordedCommandOutputAnchor( runtime: *const TranscriptRuntime, entry_id: u32,) !void { try std.testing.expect(runtime.full_transcript.depth.active()); try std.testing.expectEqual(@as(u32, 3), runtime.full_transcript.scroll_rows); try std.testing.expect(!runtime.full_transcript.follow_tail); try std.testing.expectEqual(entry_id, runtime.full_transcript.anchor_entry_id.?); try std.testing.expect(runtime.full_transcript.anchor_pending);}
test "visible recorded command output preserves a capped canonical anchor without pruning" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const status_entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "canonical command status prefix extends beyond the capped cache\nretained status tail\n", ); var metrics: Metrics = .{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
const styles = commandOutputAnsiTestStyles(); const retained_before = transcript_store.retainedStructuredBytes(&runtime); runtime.max_retained_transcript_bytes = retained_before + 512;
try runtime.writeCommandOutputChunk( alloc, &metrics, styles, .stdout, "visible\n", true, );
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expectEqual(status_entry_id, runtime.entries.items[0].id()); try std.testing.expect(runtime.entries.items[0].raw_bytes.lifecycle_pinned); try std.testing.expectEqual(RawEntryClass.command_output, runtime.entries.items[1].raw_bytes.class); try std.testing.expectEqualStrings( "\x1b[2m│ visible\x1b[0m", runtime.entries.items[1].raw_bytes.bytes, ); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(@as(usize, 1), block.total_lines); try std.testing.expectEqual(@as(usize, 1), block.lines.items.len); try std.testing.expectEqual(@as(usize, 1), block.live_entry_ids.items.len); try std.testing.expectEqual(runtime.entries.items[1].id(), block.live_entry_ids.items[0]); try std.testing.expect( transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes, ); var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expect(std.mem.startsWith( u8, appended_source.bytes, committed_source.bytes, ));}
test "visible recorded command output survives consolidation" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc);
const fixture = try setupRecordedCommandOutputAtomicFixture(&runtime, alloc); var metrics: Metrics = .{}; const styles = commandOutputAnsiTestStyles(); const retained_before = transcript_store.retainedStructuredBytes(&runtime); runtime.max_retained_transcript_bytes = retained_before + 512;
try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "visible\n", true, ); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].lines.items.len); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items[0].live_entry_ids.items.len); const live_entry_id = runtime.command_output_blocks.items[0].live_entry_ids.items[0]; runtime.full_transcript.anchor_entry_id = live_entry_id;
try runtime.flushCommandOutputSummaryForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, true, );
const block = runtime.command_output_blocks.items[0]; try std.testing.expectEqual(@as(usize, 0), block.live_entry_ids.items.len); try std.testing.expectEqualSlices(u32, &.{live_entry_id}, block.source_entry_ids.items); try std.testing.expectEqual(live_entry_id, block.entry_id.?); try std.testing.expectEqual( live_entry_id, runtime.toolDetailForEntry(fixture.status_entry_id).?.command_output_entry_id.?, ); try std.testing.expectEqual(live_entry_id, runtime.fullTranscriptAnchorEntryId().?);
var consolidated_source = try runtime.prepareTranscriptSource(alloc, null); defer consolidated_source.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), std.mem.count(u8, consolidated_source.bytes, "visible"));
runtime.setCommandOutputRenderPolicy(styles); _ = try command_output_runtime.syncCommandOutputBlockEntries(&runtime, alloc); var synchronized_source = try runtime.prepareTranscriptSource(alloc, null); defer synchronized_source.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), std.mem.count(u8, synchronized_source.bytes, "visible"));
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const projected = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 32, 0, null, ); defer alloc.free(projected); try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, projected, "visible")); try std.testing.expect((try full_transcript_screen.measureProjectionInterruptible( alloc, &projection, null, runtime.layout.cols, null, )).anchor_row != null);}
test "retention capped command output retargets a missing source anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const missing_entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, "│ pruned\n", .command_output, ); const retained_entry_id = try runtime.appendRawTranscriptEntryClassified( alloc, "│ retained\n", .command_output, ); try runtime.command_output_blocks.append(alloc, .{ .total_lines = 2 }); try runtime.command_output_blocks.items[0].live_entry_ids.append(alloc, missing_entry_id); try runtime.command_output_blocks.items[0].live_entry_ids.append(alloc, retained_entry_id); runtime.command_output_display.open_command_block = 0; runtime.full_transcript.depth = .full; runtime.full_transcript.anchor_entry_id = missing_entry_id; try removeRawEntriesForTest(&runtime, alloc, &.{missing_entry_id});
var metrics: Metrics = .{}; const styles = commandOutputAnsiTestStyles(); try runtime.flushCommandOutputSummary(alloc, &metrics, styles, true);
try std.testing.expectEqual(retained_entry_id, runtime.command_output_blocks.items[0].entry_id.?); try std.testing.expectEqual(retained_entry_id, runtime.fullTranscriptAnchorEntryId().?); var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const measurement = try full_transcript_screen.measureProjectionInterruptible( alloc, &projection, null, runtime.layout.cols, null, ); try std.testing.expect(measurement.anchor_row != null);}
fn checkVisibleRecordedCommandOutputAllocationFailuresImpl(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), }; defer runtime.deinit(alloc); const fixture = try setupRecordedCommandOutputAtomicFixture(&runtime, alloc);
const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const repaint_count_before = runtime.render_requests.pendingReasonCount(); var metrics = Metrics{}; const styles = commandOutputAnsiTestStyles();
runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "visible\n", true, ) catch |err| { try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items.len); try std.testing.expect(runtime.command_output_display.open_command_block == null); try std.testing.expectEqualDeep(command_output_runtime.CommandOutputRenderPolicy{}, runtime.command_output_render); try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expect(runtime.toolDetailForEntry(fixture.status_entry_id).?.command_output_entry_id == null); try expectRecordedCommandOutputAnchor(&runtime, fixture.status_entry_id); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
try std.testing.expectEqual(entry_count_before + 1, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before +% 1, runtime.next_entry_id); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); const block = runtime.command_output_blocks.items[0]; try std.testing.expect(command_output_runtime.sameLifecycleId(block.lifecycle_id, fixture.lifecycle_id)); try std.testing.expectEqual(@as(usize, 1), block.lines.items.len); try std.testing.expectEqual(command_output_content.Stream.stdout, block.lines.items[0].stream); try std.testing.expectEqualStrings("visible", block.lines.items[0].text); try std.testing.expectEqual(@as(usize, 1), block.live_entry_ids.items.len); try std.testing.expectEqual(runtime.entries.items[entry_count_before].id(), block.live_entry_ids.items[0]); try std.testing.expectEqual(RawEntryClass.command_output, runtime.entries.items[entry_count_before].raw_bytes.class); try std.testing.expectEqualStrings("\x1b[2m│ visible\x1b[0m", runtime.entries.items[entry_count_before].raw_bytes.bytes); try std.testing.expectEqualDeep(styles, runtime.command_output_render.styles); try std.testing.expectEqual(@as(?usize, 0), runtime.command_output_display.open_command_block); try std.testing.expect(runtime.toolDetailForEntry(fixture.status_entry_id).?.command_output_entry_id == null); try expectRecordedCommandOutputAnchor(&runtime, fixture.status_entry_id); try std.testing.expectEqual(@as(usize, 1), runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
fn checkVisibleRecordedCommandOutputAllocationFailures(alloc: Allocator) !void { return checkVisibleRecordedCommandOutputAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "visible recorded command output admission is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkVisibleRecordedCommandOutputAllocationFailures, .{}, );}
fn checkRecordedCommandOutputConsolidationAllocationFailuresImpl(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8), }; defer runtime.deinit(alloc); const fixture = try setupRecordedCommandOutputAtomicFixture(&runtime, alloc); var metrics = Metrics{}; const styles = commandOutputAnsiTestStyles();
runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "one\n", true, ) catch |err| return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stderr, "two\n", true, ) catch |err| return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };
const first_live_entry_id = runtime.command_output_blocks.items[0].live_entry_ids.items[0]; runtime.full_transcript.anchor_entry_id = first_live_entry_id; runtime.render_requests.clearReason(.transcript); runtime.transcript_band_dirty = false; const rendered_before = try renderEntriesToBytes(std.testing.allocator, runtime.entries.items, 80); defer std.testing.allocator.free(rendered_before); const transcript_before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(transcript_before); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const cursor_row_before = runtime.cursor_row; const cursor_col_before = runtime.cursor_col; const retained_before = transcript_store.retainedStructuredBytes(&runtime); const repaint_count_before = runtime.render_requests.pendingReasonCount();
runtime.flushCommandOutputSummaryForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, true, ) catch |err| { const rendered_after_failure = try renderEntriesToBytes( std.testing.allocator, runtime.entries.items, 80, ); defer std.testing.allocator.free(rendered_after_failure); try std.testing.expectEqualStrings(rendered_before, rendered_after_failure); try std.testing.expectEqualStrings(transcript_before, runtime.transcript.items); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(cursor_row_before, runtime.cursor_row); try std.testing.expectEqual(cursor_col_before, runtime.cursor_col); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); const block = runtime.command_output_blocks.items[0]; try std.testing.expect(block.entry_id == null); try std.testing.expectEqual(@as(usize, 2), block.live_entry_ids.items.len); try std.testing.expectEqual(@as(usize, 0), block.source_entry_ids.items.len); try std.testing.expectEqual(@as(?usize, 0), runtime.command_output_display.open_command_block); try std.testing.expect(runtime.toolDetailForEntry(fixture.status_entry_id).?.command_output_entry_id == null); try expectRecordedCommandOutputAnchor(&runtime, first_live_entry_id); try std.testing.expectEqual(repaint_count_before, runtime.render_requests.pendingReasonCount()); try std.testing.expect(!runtime.render_requests.hasReason(.transcript)); try std.testing.expect(!runtime.transcript_band_dirty); return switch (err) { error.WriteFailed => error.OutOfMemory, else => err, }; };
const rendered_after = try renderEntriesToBytes(std.testing.allocator, runtime.entries.items, 80); defer std.testing.allocator.free(rendered_after); try std.testing.expectEqualStrings(rendered_before, rendered_after); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(retained_before, transcript_store.retainedStructuredBytes(&runtime)); try std.testing.expectEqual(@as(usize, 1), runtime.command_output_blocks.items.len); const block = runtime.command_output_blocks.items[0]; try std.testing.expect(block.entry_id != null); try std.testing.expectEqual(@as(usize, 0), block.live_entry_ids.items.len); try std.testing.expectEqual(@as(usize, 2), block.source_entry_ids.items.len); try std.testing.expectEqualStrings("one", block.lines.items[0].text); try std.testing.expectEqualStrings("two", block.lines.items[1].text); try std.testing.expect(runtime.command_output_display.open_command_block == null); try std.testing.expectEqual(block.entry_id, runtime.toolDetailForEntry(fixture.status_entry_id).?.command_output_entry_id); try expectRecordedCommandOutputAnchor(&runtime, first_live_entry_id); try std.testing.expectEqual(@as(usize, 1), runtime.render_requests.pendingReasonCount()); try std.testing.expect(runtime.render_requests.hasReason(.transcript)); try std.testing.expect(runtime.transcript_band_dirty);}
fn checkRecordedCommandOutputConsolidationAllocationFailures(alloc: Allocator) !void { return checkRecordedCommandOutputConsolidationAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "recorded command output consolidation is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkRecordedCommandOutputConsolidationAllocationFailures, .{}, );}
test "recorded command output consolidation preserves a capped canonical anchor without recovery fallback" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
var metrics: Metrics = .{}; try runtime.writeTranscript( alloc, &metrics, "canonical command output prefix extends beyond the capped cache\nretained command output prefix tail\n", true, ); const fixture = try setupRecordedCommandOutputAtomicFixture(&runtime, alloc); const styles = commandOutputAnsiTestStyles(); try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "one\n", true, ); try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stderr, "two\n", true, );
var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic(); const retained_before = transcript_store.retainedStructuredBytes(&runtime);
try runtime.flushCommandOutputSummaryForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, true, );
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); try std.testing.expectEqual( retained_before, transcript_store.retainedStructuredBytes(&runtime), ); try std.testing.expectEqual(@as(usize, 2), runtime.command_output_blocks.items[0].lines.items.len); var consolidated_source = try runtime.prepareTranscriptSource(alloc, null); defer consolidated_source.deinit(alloc); try std.testing.expectEqualStrings(committed_source.bytes, consolidated_source.bytes); switch (runtime.transcript_commit_state) { .stable => |anchor| try std.testing.expect(!anchor.normal_buffer_recovery_pending), .invalid, .recovering => return error.TestExpectedStableTranscript, }}
test "recorded command output completion does not allocate without a matching block" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8) }; defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputAnsiTestStyles();
try runtime.writeTranscript(alloc, &metrics, "existing\n", true); var no_open_failing = std.testing.FailingAllocator.init( alloc, .{ .fail_index = 0 }, ); try runtime.flushCommandOutputSummaryForLifecycle( no_open_failing.allocator(), &metrics, styles, null, true, ); try std.testing.expectEqualDeep(styles, runtime.command_output_render.styles); try std.testing.expect(runtime.command_output_display.open_command_block == null);
const open_id = types.ToolLifecycleId{ .turn_id = 81, .call_id = "open-command" }; try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, open_id, .stdout, "open\n", true, ); const entry_count_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id; const display_before = runtime.command_output_display; const mismatch_id = types.ToolLifecycleId{ .turn_id = 82, .call_id = "other-command" }; var mismatch_failing = std.testing.FailingAllocator.init( alloc, .{ .fail_index = 0 }, ); try runtime.flushCommandOutputSummaryForLifecycle( mismatch_failing.allocator(), &metrics, styles, mismatch_id, true, );
try std.testing.expectEqualDeep(styles, runtime.command_output_render.styles); try std.testing.expectEqual(entry_count_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); try std.testing.expectEqual(display_before.open_command_block, runtime.command_output_display.open_command_block);}
test "recorded command output consolidation preserves its anchor when retention changes" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 12, 8) }; defer runtime.deinit(alloc); const fixture = try setupRecordedCommandOutputAtomicFixture(&runtime, alloc); var metrics = Metrics{}; const styles = commandOutputAnsiTestStyles();
try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "one\n", true, ); try runtime.writeCommandOutputChunkForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, .stdout, "two\n", true, ); const retained_before = transcript_store.retainedStructuredBytes(&runtime); runtime.max_retained_transcript_bytes = retained_before - 1; try runtime.enableShadowVt(alloc); try installStableAnchorForTest( &runtime, alloc, "pre-retention source\n", testSelection(0), 0, 3, 1, 91, ); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime);
try runtime.flushCommandOutputSummaryForLifecycle( alloc, &metrics, styles, fixture.lifecycle_id, true, );
try std.testing.expect(transcript_store.retainedStructuredBytes(&runtime) <= runtime.max_retained_transcript_bytes); try std.testing.expect(runtime.command_output_blocks.items[0].lines.items.len < 2); try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, );}
test "writeTranscriptBytes does NOT append an entry" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 10, .cols = 80, .content_bottom = 7, .divider_top_row = 8, .input_row = 9, .divider_bottom_row = 10, .hint_row = 10 } }; defer runtime.deinit(alloc);
var metrics = Metrics{}; try runtime.initViewport(&metrics, 5); try runtime.writeTranscriptBytes(alloc, &metrics, "structured writer bytes\n", true);
try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "structured writer bytes") != null);}
test "appendReplaceableTranscriptLineSilent mirrors a raw_bytes entry" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
_ = try runtime.appendReplaceableTranscriptLineSilent(alloc, "Thinking...\n"); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expect(runtime.entries.items[0] == .raw_bytes); try std.testing.expectEqualStrings("Thinking...\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expect(runtime.replaceable_last_line);}
test "classified replaceable append stores raw entry class" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const entry_id = try runtime.appendReplaceableTranscriptLineSilentClassified(alloc, "Thinking...\n", .subagent_status);
try std.testing.expectEqual(RawEntryClass.subagent_status, runtime.rawEntryClass(entry_id).?);}
test "replaceTrailingTranscriptLineSilent swaps the trailing entry's bytes in place" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
_ = try runtime.appendReplaceableTranscriptLineSilent(alloc, "Thinking...\n"); const initial_id = runtime.entries.items[0].id();
const replaced = try runtime.replaceTrailingTranscriptLineSilent(alloc, "Thinking... done\n"); try std.testing.expect(replaced); try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(initial_id, runtime.entries.items[0].id()); try std.testing.expectEqualStrings("Thinking... done\n", runtime.entries.items[0].raw_bytes.bytes);}
test "updateRawBytesEntry swaps bytes when entry is trailing" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const entry_id = try runtime.appendReplaceableTranscriptLineSilent(alloc, "modal v1\n"); try std.testing.expect(entry_id != 0); try std.testing.expectEqualStrings("modal v1\n", runtime.entries.items[0].raw_bytes.bytes);
const ok = try runtime.updateRawBytesEntry(alloc, entry_id, "modal v2\n"); try std.testing.expect(ok); try std.testing.expectEqualStrings("modal v2\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expectEqualStrings("modal v2\n", runtime.transcript.items); try std.testing.expect(runtime.replaceable_last_line); try std.testing.expectEqual(runtime.transcript.items.len - "modal v2\n".len, runtime.replaceable_start);}
test "updateRawBytesEntry swaps bytes when entry is in middle" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const id_a = try runtime.appendReplaceableTranscriptLineSilent(alloc, "block A\n"); const dup_b = try alloc.dupe(u8, "block B\n"); _ = try runtime.appendRawBytesEntry(alloc, dup_b); try runtime.transcript.appendSlice(alloc, "block B\n");
const ok = try runtime.updateRawBytesEntry(alloc, id_a, "block A updated\n"); try std.testing.expect(ok); try std.testing.expectEqualStrings("block A updated\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expect(runtime.entries.items[1] == .raw_bytes); try std.testing.expectEqualStrings("block B\n", runtime.entries.items[1].raw_bytes.bytes); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "block A updated\n") != null); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "block B\n") != null); try std.testing.expect(!runtime.replaceable_last_line);}
test "updateRawBytesEntry returns false for unknown id" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
_ = try runtime.appendReplaceableTranscriptLineSilent(alloc, "existing\n"); const ok = try runtime.updateRawBytesEntry(alloc, 9999, "nope\n"); try std.testing.expect(!ok); try std.testing.expectEqualStrings("existing\n", runtime.entries.items[0].raw_bytes.bytes);}
test "updateRawBytesEntry returns false for non-raw_bytes entry" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const text = try alloc.dupe(u8, "hello user"); const user_id = try runtime.appendUserTurnOwned(alloc, .{ .text = text, .images = &.{} }); const ok = try runtime.updateRawBytesEntry(alloc, user_id, "hijack\n"); try std.testing.expect(!ok);}
test "updateRawBytesEntry updates modal entry in place after intervening append" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{}; defer runtime.deinit(alloc);
const modal_id = try runtime.appendReplaceableTranscriptLineSilent(alloc, "modal v1\n"); const stream_dup = try alloc.dupe(u8, "stream chunk\n"); _ = try runtime.appendRawBytesEntry(alloc, stream_dup); try runtime.transcript.appendSlice(alloc, "stream chunk\n");
const ok = try runtime.updateRawBytesEntry(alloc, modal_id, "modal v2\n"); try std.testing.expect(ok); try std.testing.expectEqual(@as(usize, 2), runtime.entries.items.len); try std.testing.expectEqualStrings("modal v2\n", runtime.entries.items[0].raw_bytes.bytes); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "modal v2\n") != null); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "stream chunk\n") != null); try std.testing.expect(std.mem.indexOf(u8, runtime.transcript.items, "modal v1") == null);}
test "tool status raw entry updates after command output appends" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = .{ .rows = 24, .cols = 80, .content_bottom = 21, .divider_top_row = 22, .input_row = 23, .divider_bottom_row = 24, .hint_row = 22, } }; defer runtime.deinit(alloc); var metrics = Metrics{};
const status_id = try runtime.appendRawTranscriptEntry(alloc, "Running npm test\n"); try runtime.writeCommandOutputChunk(alloc, &metrics, .{ .system_notice_label_style = "", .system_notice_text_style = "", .reset_style = "", .dim_style = "", .red_style = "", }, .stdout, "ok\n", true);
const updated = try runtime.updateRawBytesEntry(alloc, status_id, "Ran npm test\n"); try std.testing.expect(updated); var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "Ran npm test") != null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "│ ok") == null); try std.testing.expect(std.mem.indexOf(u8, source.bytes, "Running npm test") == null);
runtime.full_transcript.depth = .full; var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); try std.testing.expect(std.mem.indexOf(u8, full, "│ ok") != null); try std.testing.expect(std.mem.indexOf(u8, full, "Running npm test") == null);}
test "advanceCursor row advance matches visualRowsForLine - 1 for wrap-exact content" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 40, .cols = 3, .content_bottom = 37, .divider_top_row = 38, .input_row = 39, .divider_bottom_row = 40, .hint_row = 40, }, }; defer runtime.deinit(std.testing.allocator);
const text = "abcdef"; try std.testing.expectEqual(@as(u16, 2), visualRowsForLine(text, 3));
runtime.cursor_row = 1; runtime.cursor_col = 1; _ = runtime.advanceCursor(text); try std.testing.expectEqual(@as(u16, 2), runtime.cursor_row);}
test "advanceCursor row advance matches visualRowsForLine - 1 across hard newlines" { var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 40, .cols = 3, .content_bottom = 37, .divider_top_row = 38, .input_row = 39, .divider_bottom_row = 40, .hint_row = 40, }, }; defer runtime.deinit(std.testing.allocator);
const text = "abcdef\nghijkl"; try std.testing.expectEqual(@as(u16, 4), visualRowsForLine(text, 3));
runtime.cursor_row = 1; runtime.cursor_col = 1; _ = runtime.advanceCursor(text); try std.testing.expectEqual(@as(u16, 4), runtime.cursor_row);}
test "writeTranscriptBytes mid-row continuation defers scroll after first paint" { const alloc = std.testing.allocator; var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 40, .cols = 80, .content_bottom = 35, .divider_top_row = 36, .input_row = 37, .divider_bottom_row = 38, .hint_row = 39, }, }; defer runtime.deinit(alloc); runtime.cursor_row = 35; runtime.cursor_col = 40; runtime.viewport_top_row = 35; runtime.has_painted_transcript = true;
var bytes: std.ArrayList(u8) = .empty; defer bytes.deinit(alloc); var i: usize = 0; while (i < 1000) : (i += 1) try bytes.append(alloc, 'X');
var metrics: Metrics = .{}; try runtime.writeTranscriptBytes(alloc, &metrics, bytes.items, true);
try std.testing.expectEqual(@as(u16, 35), runtime.viewport_top_row); try std.testing.expectEqual(@as(u16, 35), runtime.cursor_row);}
test "appendReplaceableTranscriptLine does not emit scroll (buffer-only)" { const alloc = std.testing.allocator; var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo());
var runtime = TranscriptRuntime{ .stdout_file = sink, .layout = .{ .rows = 40, .cols = 10, .content_bottom = 35, .divider_top_row = 36, .input_row = 37, .divider_bottom_row = 38, .hint_row = 39, }, }; defer runtime.deinit(alloc); runtime.cursor_row = 35; runtime.cursor_col = 1; runtime.viewport_top_row = 35;
var bytes: std.ArrayList(u8) = .empty; defer bytes.deinit(alloc); try bytes.appendSlice(alloc, "XXXXXXXXXX"); var ln: u16 = 0; while (ln < 9) : (ln += 1) { try bytes.append(alloc, '\n'); try bytes.appendSlice(alloc, "XXXXXXXXXX"); }
var metrics: Metrics = .{}; _ = try runtime.appendReplaceableTranscriptLine(alloc, &metrics, bytes.items);
try std.testing.expectEqual(@as(u16, 35), runtime.viewport_top_row); try std.testing.expectEqual(@as(u16, 40), runtime.cursor_row);}
test "streamed chunk-split produces same viewport as single emission" { const alloc = std.testing.allocator;
var sink1 = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink1.close(io_mod.getIo()); var sink2 = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink2.close(io_mod.getIo());
const layout: Layout = .{ .rows = 40, .cols = 80, .content_bottom = 35, .divider_top_row = 36, .input_row = 37, .divider_bottom_row = 38, .hint_row = 39, };
var single = TranscriptRuntime{ .stdout_file = sink1, .layout = layout }; defer single.deinit(alloc); single.cursor_row = 35; single.cursor_col = 1; single.viewport_top_row = 35;
var split = TranscriptRuntime{ .stdout_file = sink2, .layout = layout }; defer split.deinit(alloc); split.cursor_row = 35; split.cursor_col = 1; split.viewport_top_row = 35;
var full: std.ArrayList(u8) = .empty; defer full.deinit(alloc); var i: usize = 0; while (i < 2000) : (i += 1) try full.append(alloc, 'X');
var half: std.ArrayList(u8) = .empty; defer half.deinit(alloc); i = 0; while (i < 1000) : (i += 1) try half.append(alloc, 'X');
var m1: Metrics = .{}; var m2: Metrics = .{}; try single.writeTranscriptBytes(alloc, &m1, full.items, true); try split.writeTranscriptBytes(alloc, &m2, half.items, true); try split.writeTranscriptBytes(alloc, &m2, half.items, true);
try std.testing.expectEqual(single.viewport_top_row, split.viewport_top_row); try std.testing.expectEqual(single.cursor_row, split.cursor_row); try std.testing.expectEqual(single.cursor_col, split.cursor_col);}
fn lifecycleTestRuntime(max_retained_bytes: ?usize) TranscriptRuntime { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 24, 20), }; if (max_retained_bytes) |value| runtime.max_retained_transcript_bytes = value; return runtime;}
fn lifecycleId(turn_id: u64, call_id: []const u8) types.ToolLifecycleId { return .{ .turn_id = turn_id, .call_id = call_id };}
fn startLifecycle( runtime: *TranscriptRuntime, alloc: Allocator, turn_id: u64, call_id: []const u8,) !u32 { _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = lifecycleId(turn_id, call_id), .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); return runtime.toolActivityRecord(lifecycleId(turn_id, call_id)).?.entry_id;}
test "current compact projection groups tool rows without mutating entries" { const alloc = std.testing.allocator; defer user_message_card.setStyle(false, null); user_message_card.setStyle(false, null); var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const read_id = lifecycleId(1, "read"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = read_id, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = read_id, .outcome = .{ .kind = .completed, .summary = "Read file" }, } });
const edit_id = lifecycleId(1, "edit"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = edit_id, .reconciles_provisional_call_id = null, .tool_name = "edit_file", .activity_kind = .edit, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = edit_id, .outcome = .{ .kind = .failed, .summary = "Edit failed" }, } });
const entry_count = runtime.entries.items.len; var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); try std.testing.expect(std.mem.find( u8, compact.bytes, "\x1b[38;5;255m●\x1b[0m \x1b[38;5;245m2 tool calls · 1 read · 1 edit · 1 failed\x1b[0m\n" ++ "\x1b[38;5;245m├ Read file\x1b[0m\n" ++ "\x1b[38;5;245m└ Edit failed\x1b[0m", ) != null); try std.testing.expectEqual(entry_count, runtime.entries.items.len);}
test "current compact projection finalizes tool groups from turn finished fallbacks" { const alloc = std.testing.allocator; const cases = [_]struct { call_id: []const u8, provisional: bool, turn_outcome: types.TurnPresentationOutcome, detail_outcome: types.ToolOutcomeKind, fallback_disposition: ?transcript_blocks.ToolFallbackDisposition, summary_text: []const u8, }{ .{ .call_id = "unreported", .provisional = false, .turn_outcome = .completed, .detail_outcome = .completed, .fallback_disposition = .completion_unreported, .summary_text = "1 tool call · 1 read · 1 unreported" }, .{ .call_id = "not-executed", .provisional = true, .turn_outcome = .completed, .detail_outcome = .completed, .fallback_disposition = .not_executed, .summary_text = "1 tool call · 1 read · 1 not executed" }, .{ .call_id = "interrupted", .provisional = false, .turn_outcome = .interrupted, .detail_outcome = .cancelled, .fallback_disposition = null, .summary_text = "1 cancelled" }, .{ .call_id = "failed", .provisional = false, .turn_outcome = .failed, .detail_outcome = .failed, .fallback_disposition = null, .summary_text = "Tool failed" }, };
for (cases) |case| { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, case.call_id); if (case.provisional) { _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "read_file", .activity_kind = .read, } }); } else { _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); } const entry_id = runtime.toolActivityRecord(id).?.entry_id;
_ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = case.turn_outcome, } });
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expect(std.mem.find(u8, rendered.bytes, case.summary_text) != null); try std.testing.expect(std.mem.find(u8, rendered.bytes, "running") == null); try std.testing.expectEqual( case.detail_outcome, runtime.toolDetailForEntry(entry_id).?.outcome.?, ); try std.testing.expectEqual( case.fallback_disposition, runtime.toolDetailForEntry(entry_id).?.fallback_disposition, ); }}
fn checkCompactParallelFallbackAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); const ids = [_]types.ToolLifecycleId{ lifecycleId(1, "provisional-a"), lifecycleId(1, "provisional-b"), }; var entry_ids: [ids.len]u32 = undefined; for (ids, 0..) |id, index| { _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "read_file", .activity_kind = .read, } }); entry_ids[index] = runtime.toolActivityRecord(id).?.entry_id; }
var source_before = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_before.deinit(std.testing.allocator); _ = runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed, } }) catch |err| { var source_after = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_after.deinit(std.testing.allocator); try std.testing.expectEqualStrings(source_before.bytes, source_after.bytes); for (ids) |id| { try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .provisional); } return err; };
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expect(std.mem.find( u8, rendered.bytes, "2 tool calls · 2 read · 2 not executed", ) != null); try std.testing.expect(std.mem.find(u8, rendered.bytes, "running") == null); for (entry_ids) |entry_id| { try std.testing.expectEqual( types.ToolOutcomeKind.completed, runtime.toolDetailForEntry(entry_id).?.outcome.?, ); try std.testing.expectEqual( transcript_blocks.ToolFallbackDisposition.not_executed, runtime.toolDetailForEntry(entry_id).?.fallback_disposition.?, ); }}
fn checkCompactParallelFallbackAllocationFailures(alloc: Allocator) !void { return checkCompactParallelFallbackAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "compact parallel fallbacks are atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkCompactParallelFallbackAllocationFailures, .{}, );}
fn prepareTurnFinishedAnchor(runtime: *TranscriptRuntime, alloc: Allocator) !void { try runtime.enableShadowVt(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "pre 0\npre 1\npre 2\npre 3\npre 4\npre 5\n", .subagent_status, ); _ = try startLifecycle(runtime, alloc, 1, "unfinished"); try installStableAnchorForTest( runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, );}
test "preserving lifecycle policy keeps anchor for turn finished fallback rewrite" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try prepareTurnFinishedAnchor(&runtime, alloc);
_ = try runtime.applyToolLifecyclePreservingNormalBufferAnchor(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .interrupted, }, });
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );}
test "preserving batch lifecycle rewrite keeps a capped canonical anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, ); const first_entry_id = try startLifecycle(&runtime, alloc, 1, "first"); const second_entry_id = try startLifecycle(&runtime, alloc, 1, "second");
var metrics: Metrics = .{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
_ = try runtime.applyToolLifecyclePreservingNormalBufferAnchor(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .interrupted, }, });
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); try std.testing.expect(std.mem.find( u8, runtime.toolStatusEntryLabel(first_entry_id).?, "Tool cancelled", ) != null); try std.testing.expect(std.mem.find( u8, runtime.toolStatusEntryLabel(second_entry_id).?, "Tool cancelled", ) != null);}
test "turn finished anchor preservation keeps a same-epoch retention rewrite" { const alloc = std.testing.allocator;
var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try prepareTurnFinishedAnchor(&runtime, alloc); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime); runtime.max_retained_transcript_bytes = 0;
_ = try runtime.applyToolLifecyclePreservingNormalBufferAnchor(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .interrupted }, });
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, );}
test "turn finished anchor preservation falls back for strict mode or geometry blocker" { const alloc = std.testing.allocator;
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try prepareTurnFinishedAnchor(&runtime, alloc); runtime.max_transcript_bytes = 1;
_ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .interrupted }, });
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.invalid, runtime.transcriptCommitDiagnostic().state, ); }
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try prepareTurnFinishedAnchor(&runtime, alloc); runtime.layout.cols = 39;
_ = try runtime.applyToolLifecyclePreservingNormalBufferAnchor(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .interrupted }, });
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.invalid, runtime.transcriptCommitDiagnostic().state, ); }}
test "untrimmed pinned status replacement preserves anchor through scroll transition" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "pre 0\npre 1\npre 2\npre 3\npre 4\npre 5\n", .subagent_status, ); const entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", ); try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, );
try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, entry_id, "running\n", )); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); try setPreparedVisualOffsetForTest(&prepared, 2);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(!facts.source_compatible); try std.testing.expect(facts.recovery_rebase); try std.testing.expectEqual(@as(u16, 2), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), facts.semantic_progress_rows); try std.testing.expectEqual(@as(u16, 2), facts.planned_rows);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); try std.testing.expectEqual(@as(u16, 2), scroll_plan.terminal_scroll_rows); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc); try std.testing.expect(transition.document_append.isEmpty()); runtime.consumeTranscriptTransition(alloc, &transition, .{ .bytes_written = 1, .changed_cells = 1, .full_repaint = true, .terminal_scroll_rows_committed = scroll_plan.terminal_scroll_rows, .document_append_committed = false, .committed_cursor_row = transition.cursor_row, .committed_cursor_col = transition.cursor_col, .shadow_state = .committed, .next_invalidation = render_engine.paint_plan.FrameInvalidationSet.empty(), }); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); try std.testing.expect(runtime.transcript_cache_origin_untrimmed);
var next_source = try runtime.prepareTranscriptSource(alloc, null); defer next_source.deinit(alloc); try std.testing.expect( runtime.stableTranscriptProjectionForFlow(next_source.bytes) != null, );}
test "command terminal replacement preserves committed scrollback anchor" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "pre 0\npre 1\npre 2\npre 3\npre 4\npre 5\n", .subagent_status, ); const id = lifecycleId(1, "cancelled-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"sleep 30\"}", } }); try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, );
_ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .cancelled, .summary = "Command cancelled" }, .result_memory = .{ .command_process_presentation = .{ .exit_code = 130 } }, } });
try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, ); const entry_id = runtime.toolActivityRecord(id).?.entry_id; try std.testing.expect(std.mem.find( u8, runtime.toolStatusEntryLabel(entry_id).?, "Command cancelled", ) != null); try std.testing.expect(runtime.toolDetailForEntry(entry_id).?.command_output_entry_id != null);}
test "pinned status admission preserves an authoritative committed anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, ); const first_status_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_before = runtime.transcriptCommitDiagnostic();
try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, first_status_id, "running\n", )); try std.testing.expectEqualDeep( committed_before, runtime.transcriptCommitDiagnostic(), );
_ = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", );
try std.testing.expectEqualDeep( committed_before, runtime.transcriptCommitDiagnostic(), ); var appended_source = try runtime.prepareTranscriptSource(alloc, null); defer appended_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, appended_source.bytes, "LONG_PASTE_LAST_MARKER"), ); try std.testing.expect(std.mem.find(u8, runtime.toolStatusEntryLabel(first_status_id).?, "running") != null); try std.testing.expectEqual(@as(usize, 2), runtime.lifecyclePinCount());}
test "pinned status admission preserves an anchor when retention prunes its source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "anchored transcript\n", .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime); runtime.max_retained_transcript_bytes = "pending\n".len;
_ = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", );
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, ); var retained_source = try runtime.prepareTranscriptSource(alloc, null); defer retained_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, retained_source.bytes, "anchored transcript"), ); try std.testing.expectEqual(@as(usize, 1), runtime.lifecyclePinCount());}
test "pinned status replacement preserves authoritative cache changes and rebases retention or geometry" { const alloc = std.testing.allocator;
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "ordinary retained entry\n", .subagent_status, ); const entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", ); runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime); try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, ); try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, entry_id, "replacement that requires structured retention\n", )); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.invalid, runtime.transcriptCommitDiagnostic().state, ); }
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); const entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "old\n", ); runtime.max_transcript_bytes = runtime.transcript.items.len; try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, ); const committed_before = runtime.transcriptCommitDiagnostic();
try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, entry_id, "replacement that crosses the visible cache cap\n", )); try std.testing.expectEqualDeep( committed_before, runtime.transcriptCommitDiagnostic(), ); }
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, .max_transcript_bytes = 5, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); const entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "old\npinned\nstatus", ); try std.testing.expect(runtime.transcript.items.len <= runtime.max_transcript_bytes); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_before = runtime.transcriptCommitDiagnostic();
try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, entry_id, "ok", )); try std.testing.expectEqualDeep( committed_before, runtime.transcriptCommitDiagnostic(), ); }
{ var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc); const entry_id = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "pending\n", ); try installStableAnchorForTest( &runtime, alloc, runtime.transcript.items, testSelection(0), 0, 4, 1, 1, );
runtime.layout.cols = 20; try std.testing.expect(try transcript_store.replacePinnedToolStatusAtomic( &runtime, alloc, entry_id, "running\n", )); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.invalid, runtime.transcriptCommitDiagnostic().state, ); }}
test "visual epoch retains full history across repeated clears and compact rebuilds" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "old welcome\n", .welcome); const old_id = try runtime.appendRawTranscriptEntryClassified(alloc, "retained answer\n", .subagent_status); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "history", .tone = .information, .body = "retained notice", }); const retained_bytes = runtime.entries.items[1].raw_bytes.bytes.ptr;
for (0..3) |_| { try runtime.resetVisualEpoch(alloc, "current welcome\n"); try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expectEqual(old_id, runtime.entries.items[1].id()); try std.testing.expectEqual(retained_bytes, runtime.entries.items[1].raw_bytes.bytes.ptr); try std.testing.expect(runtime.entries.items[1].raw_bytes.inline_hidden); try std.testing.expect(runtime.entries.items[2].semantic_notice.inline_hidden); try std.testing.expectEqualStrings("current welcome\n", runtime.transcript.items);
var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); try std.testing.expect(std.mem.find(u8, compact.bytes, "retained") == null);
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, full, "retained answer")); try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, full, "retained notice")); try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, full, "current welcome")); try std.testing.expect(std.mem.find(u8, full, "old welcome") == null); }
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "new answer\n", .subagent_status); var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); try std.testing.expect(std.mem.find(u8, compact.bytes, "new answer") != null); try std.testing.expect(std.mem.find(u8, compact.bytes, "retained") == null);
runtime.clearTranscript(alloc); try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.transcript.items.len);}
test "visual epoch preserves monotonic entry IDs across repeated clears" { const alloc = std.testing.allocator; for ([_]bool{ true, false }) |has_original_welcome| { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); if (has_original_welcome) { _ = try runtime.appendRawTranscriptEntryClassified(alloc, "original welcome\n", .welcome); runtime.entries.items[0].raw_bytes.created_at_ms = 1234; } var ordinary_ids: [300]u32 = undefined; for (&ordinary_ids) |*id| { id.* = try runtime.appendRawTranscriptEntryClassified(alloc, "retained answer\n", .subagent_status); } const welcome_index: usize = if (has_original_welcome) 0 else ordinary_ids.len; const welcome_id = if (has_original_welcome) runtime.entries.items[0].id() else runtime.next_entry_id; var welcome_created_at: ?i64 = if (has_original_welcome) 1234 else null; const expected_next_id = runtime.next_entry_id + @as(u32, if (has_original_welcome) 0 else 1);
for (0..3) |_| { try runtime.resetVisualEpoch(alloc, "current welcome\n"); try std.testing.expectEqual(@as(usize, ordinary_ids.len + 1), runtime.entries.items.len); try std.testing.expectEqual(expected_next_id, runtime.next_entry_id); const welcome_entry = runtime.entries.items[welcome_index].raw_bytes; try std.testing.expectEqual(welcome_id, welcome_entry.id); try std.testing.expectEqualStrings("current welcome\n", welcome_entry.bytes); if (welcome_created_at) |created_at| { try std.testing.expectEqual(created_at, welcome_entry.created_at_ms); } else { welcome_created_at = welcome_entry.created_at_ms; } var welcome_count: usize = 0; var ordinary_index: usize = 0; for (runtime.entries.items, 0..) |entry, index| { if (index > 0) try std.testing.expect(runtime.entries.items[index - 1].id() < entry.id()); if (entry == .raw_bytes and entry.raw_bytes.class == .welcome) { welcome_count += 1; } else { try std.testing.expectEqual(ordinary_ids[ordinary_index], entry.id()); try std.testing.expect(entry.raw_bytes.inline_hidden); ordinary_index += 1; } } try std.testing.expectEqual(@as(usize, 1), welcome_count); try std.testing.expectEqual(ordinary_ids.len, ordinary_index); try std.testing.expectEqualStrings("current welcome\n", runtime.transcript.items); } }}
fn checkVisualEpochAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "original welcome\n", .welcome); const old_id = try runtime.appendRawTranscriptEntryClassified(alloc, "borrowed answer\n", .subagent_status); const pinned_id = try transcript_store.appendPinnedToolStatusAtomic(&runtime, alloc, "running\n"); const old_entries = runtime.entries.items.ptr; const old_payload = runtime.entries.items[1].raw_bytes.bytes.ptr; const old_next_id = runtime.next_entry_id; const before = try std.testing.allocator.dupe(u8, runtime.transcript.items); defer std.testing.allocator.free(before);
runtime.resetVisualEpoch(alloc, "replacement welcome\n") catch |err| { try std.testing.expectEqual(old_entries, runtime.entries.items.ptr); try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expectEqual(old_next_id, runtime.next_entry_id); try std.testing.expectEqualStrings(before, runtime.transcript.items); try std.testing.expectEqual(old_payload, runtime.entries.items[1].raw_bytes.bytes.ptr); try std.testing.expect(!runtime.entries.items[1].raw_bytes.inline_hidden); try std.testing.expectEqualStrings("borrowed answer\n", runtime.entries.items[1].raw_bytes.bytes); try std.testing.expect(runtime.isLifecyclePinned(pinned_id)); return err; }; try std.testing.expectEqual(old_id, runtime.entries.items[1].id()); try std.testing.expectEqual(old_payload, runtime.entries.items[1].raw_bytes.bytes.ptr); try std.testing.expect(runtime.entries.items[1].raw_bytes.inline_hidden); try std.testing.expect(runtime.isLifecyclePinned(pinned_id));}
fn checkVisualEpochAllocationFailures(alloc: Allocator) !void { return checkVisualEpochAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "visual epoch starts a visible assistant tail without resurrecting cleared text" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); var metrics = Metrics{}; _ = try runtime.appendRawTranscriptEntryClassified(alloc, "welcome\n", .welcome); const before_id = try runtime.streamAssistantChunk(alloc, &metrics, "before clear"); try runtime.resetVisualEpoch(alloc, "welcome\n"); const after_id = try runtime.streamAssistantChunk(alloc, &metrics, "after clear"); try std.testing.expect(before_id != after_id); try std.testing.expectEqualStrings("before clear", runtime.lookupAssistantSegments(before_id).?.text.items); var compact = try runtime.prepareTranscriptSource(alloc, null); defer compact.deinit(alloc); try std.testing.expect(std.mem.find(u8, compact.bytes, "before clear") == null); try std.testing.expect(std.mem.find(u8, compact.bytes, "after clear") != null);}
test "visual epoch reset is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkVisualEpochAllocationFailures, .{}, );}
test "visual epoch hides ordinary entries and preserves live tool identities through settlement" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified(alloc, "original welcome\n", .welcome); _ = try runtime.appendRawTranscriptEntryClassified(alloc, "old transcript\n", .subagent_status); const entry_id = try startLifecycle(&runtime, alloc, 7, "call-7");
try runtime.resetVisualEpoch(alloc, "welcome\n");
try std.testing.expect(std.mem.startsWith(u8, runtime.transcript.items, "welcome\n")); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "old transcript") == null); try std.testing.expectEqual(entry_id, runtime.toolActivityRecord(lifecycleId(7, "call-7")).?.entry_id); try std.testing.expect(runtime.isLifecyclePinned(entry_id));
_ = try runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = lifecycleId(7, "call-7"), .text = "still running", } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(7, "call-7"), .outcome = .{ .kind = .completed, .summary = "finished" }, } }); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "finished") != null); try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[2].id()); try std.testing.expect(!runtime.entries.items[2].raw_bytes.inline_hidden);
_ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 7, .outcome = .completed, } }); try runtime.finishLifecycleBatch(alloc); try std.testing.expect(!runtime.isLifecyclePinned(entry_id)); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "finished") != null); try runtime.resetVisualEpoch(alloc, "welcome again\n"); try std.testing.expectEqual(@as(usize, 3), runtime.entries.items.len); try std.testing.expectEqual(entry_id, runtime.entries.items[2].id()); try std.testing.expect(runtime.entries.items[2].raw_bytes.inline_hidden); try std.testing.expectEqualStrings("welcome again\n", runtime.transcript.items);
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); try std.testing.expect(std.mem.find(u8, full, "old transcript") != null); try std.testing.expect(std.mem.find(u8, full, "finished") != null);}
test "visual epoch retains command output blocks and detail associations" { const alloc = std.testing.allocator; var sink = try std.Io.Dir.openFileAbsolute(io_mod.getIo(), "/dev/null", .{ .mode = .write_only }); defer sink.close(io_mod.getIo()); var runtime = commandOutputTestRuntime(sink); defer runtime.deinit(alloc); var metrics = Metrics{}; const styles = commandOutputTestStyles(); const id = lifecycleId(1, "retained-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "shell", .activity_kind = .command, .arguments_json = "{\"request\":{\"action\":\"run\",\"command\":\"true\"}}", } }); const entry_id = runtime.toolActivityRecord(id).?.entry_id; try runtime.writeCommandOutputChunkForLifecycle(alloc, &metrics, styles, id, .stdout, "retained command result\n", true); try runtime.flushCommandOutputSummaryForLifecycle(alloc, &metrics, styles, id, true); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran true" }, .result = "retained command result\n", .result_memory = .{ .command_process_presentation = .{ .exit_code = 0 } }, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed, } }); try runtime.finishLifecycleBatch(alloc); const output_id = runtime.toolDetailForEntry(entry_id).?.command_output_entry_id.?; const block_count = runtime.command_output_blocks.items.len; try std.testing.expect(block_count > 0); const blocks_ptr = runtime.command_output_blocks.items.ptr;
for (0..2) |_| { try runtime.resetVisualEpoch(alloc, "welcome\n"); try std.testing.expectEqual(block_count, runtime.command_output_blocks.items.len); try std.testing.expectEqual(blocks_ptr, runtime.command_output_blocks.items.ptr); try std.testing.expectEqual(output_id, runtime.toolDetailForEntry(entry_id).?.command_output_entry_id.?); try std.testing.expect(transcriptContainsEntry(&runtime, output_id)); try std.testing.expectEqualStrings("welcome\n", runtime.transcript.items); var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); // The row renders styled; assert on the command text inside it. try std.testing.expect(std.mem.find(u8, full, "true") != null); try std.testing.expect(std.mem.find(u8, full, "retained command result") != null); } runtime.clearTranscript(alloc); try std.testing.expectEqual(@as(usize, 0), runtime.command_output_blocks.items.len); try std.testing.expectEqual(@as(usize, 0), runtime.tool_details.items.len);}
test "transcript lifecycle identity updates are idempotent and atomic" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
for ([_]?[]const u8{ null, "read_file", "read_file" }) |tool_name| { _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = lifecycleId(1, "call"), .presentation_group_id = .{ .turn_id = 1, .anchor_step_id = 11 }, .tool_name = tool_name, .activity_kind = .read, } }); } const provisional_id = lifecycleId(1, "call"); const entry_id = runtime.toolActivityRecord(provisional_id).?.entry_id; try std.testing.expectEqualStrings( "read_file", runtime.toolActivityRecord(provisional_id).?.tool_name.?, ); // Force record-map growth so stale pointers fail deterministically. inline for ([_][]const u8{ "alias", "target", "fill-1", "fill-2", "fill-3" }) |call_id| { _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = lifecycleId(1, call_id), .tool_name = "read_file", .activity_kind = .read, } }); } const authoritative_id = lifecycleId(1, "authoritative-" ++ ("x" ** 800)); const record_capacity_before = runtime.lifecycle_state.records.capacity(); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = authoritative_id, .presentation_group_id = .{ .turn_id = 1, .anchor_step_id = 12 }, .reconciles_provisional_call_id = "call", .tool_name = "read_file", .activity_kind = .read, } }); try std.testing.expect(runtime.lifecycle_state.records.capacity() > record_capacity_before); try std.testing.expect(runtime.toolActivityRecord(provisional_id) == null); try std.testing.expectEqual( entry_id, runtime.toolActivityRecord(authoritative_id).?.entry_id, ); try std.testing.expectEqual( types.ToolPresentationGroupId{ .turn_id = 1, .anchor_step_id = 11 }, runtime.toolDetailForEntry(entry_id).?.presentation_group_id.?, ); const alias_before = runtime.toolActivityRecord(lifecycleId(1, "alias")).?.entry_id; const target_before = runtime.toolActivityRecord(lifecycleId(1, "target")).?.entry_id; try std.testing.expectError( error.LifecycleReconciliationCollision, runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = lifecycleId(1, "target"), .reconciles_provisional_call_id = "alias", .tool_name = "glob_files", .activity_kind = .list, } }), ); try std.testing.expectEqual(alias_before, runtime.toolActivityRecord(lifecycleId(1, "alias")).?.entry_id); try std.testing.expectEqual(target_before, runtime.toolActivityRecord(lifecycleId(1, "target")).?.entry_id); const records_before = runtime.toolActivityRecordCount(); try std.testing.expectError( error.UnknownToolLifecycleIdentity, runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = lifecycleId(1, "missing"), .text = "progress", } }), ); try std.testing.expectError( error.UnknownToolLifecycleIdentity, runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(1, "missing"), .outcome = .{ .kind = .failed, .summary = "failed" }, } }), ); try std.testing.expectEqual(records_before, runtime.toolActivityRecordCount()); runtime.clearTranscript(alloc); try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount()); try std.testing.expectEqual(@as(usize, 0), runtime.lifecyclePinCount()); try std.testing.expectEqual(@as(usize, 0), runtime.entries.items.len);}
test "authoritative lifecycle can place a provisional row after the latest transcript entry" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, "command"); _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "run_command", .activity_kind = .command, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = id, .text = "Preparing command", } }); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "permissions", .tone = .success, .body = "Auto agent approved this request: Running command.", .visibility = .full_only, });
_ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"printf approved\"}", .place_after_current_transcript = true, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = id, .text = "Running printf approved", } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran printf approved" }, } });
try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "Auto agent approved") == null); try std.testing.expect(std.mem.find(u8, runtime.transcript.items, "Ran printf approved") != null);
var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); // Tool rows style the command verb; compare against plain text. const plain = try plainTextForTest(alloc, full); defer alloc.free(plain); const notice_index = std.mem.find(u8, plain, "Auto agent approved").?; const completed_index = std.mem.find(u8, plain, "Ran printf approved").?; try std.testing.expect(notice_index < completed_index);}
fn plainTextForTest(alloc: std.mem.Allocator, styled: []const u8) ![]u8 { var out: std.ArrayList(u8) = .empty; errdefer out.deinit(alloc); var i: usize = 0; while (i < styled.len) { const next = display_width.ansiSequenceEnd(styled, i); if (next != i) { i = next; continue; } try out.append(alloc, styled[i]); i += 1; } return out.toOwnedSlice(alloc);}
test "coalesced approval lifecycle reposition preserves an authoritative committed anchor" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, );
var metrics = Metrics{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); const committed_offset = preparedVisualOffsetForTest(&committed_prepared); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, committed_offset, committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
const id = lifecycleId(91, "coalesced-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "run_command", .activity_kind = .command, } }); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "permissions", .tone = .success, .body = "Auto agent approved this request: Running command.", .visibility = .full_only, }); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"seq 1 1\"}", .place_after_current_transcript = true, } });
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); var repositioned_source = try runtime.prepareTranscriptSource(alloc, null); defer repositioned_source.deinit(alloc); try std.testing.expect(std.mem.find(u8, repositioned_source.bytes, "Auto agent approved") == null); try std.testing.expect(std.mem.find( u8, repositioned_source.bytes, "run_command", ) != null); var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); const notice_index = std.mem.find(u8, full, "Auto agent approved") orelse return error.TestExpectedEqual; const full_status_index = std.mem.find(u8, full, "run_command") orelse return error.TestExpectedEqual; try std.testing.expect(notice_index < full_status_index);
var repositioned_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer repositioned_prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&repositioned_prepared); try std.testing.expect(facts.source_compatible); try std.testing.expect(facts.semantic_rows > 0); try std.testing.expectEqual(committed_offset, facts.source_visual_offset);}
test "coalesced approval lifecycle reposition preserves its anchor when retention prunes its source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const anchored = "anchored transcript\n"; _ = try runtime.appendRawTranscriptEntryClassified( alloc, anchored, .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime);
const id = lifecycleId(92, "retained-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "run_command", .activity_kind = .command, } }); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "permissions", .tone = .success, .body = "Auto agent approved this request: Running command.", .visibility = .full_only, }); runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime) - anchored.len;
_ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"seq 1 1\"}", .place_after_current_transcript = true, } });
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, ); var retained_source = try runtime.prepareTranscriptSource(alloc, null); defer retained_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, retained_source.bytes, "anchored transcript"), ); try std.testing.expect(std.mem.find(u8, retained_source.bytes, "Auto agent approved") == null); try std.testing.expect(std.mem.find( u8, retained_source.bytes, "run_command", ) != null); var projection = try runtime.buildFullTranscriptProjection(alloc, null); defer projection.deinit(alloc); const full = try full_transcript_screen.renderProjectionViewportSourceInterruptible( alloc, &projection, null, runtime.layout.cols, 64, 0, null, ); defer alloc.free(full); const notice_index = std.mem.find(u8, full, "Auto agent approved") orelse return error.TestExpectedEqual; const status_index = std.mem.find(u8, full, "run_command") orelse return error.TestExpectedEqual; try std.testing.expect(notice_index < status_index);}
test "transcript lifecycle preserves caller resolved activity metadata" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, "custom-browser"); const started_kind = (try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "custom_probe_tool", .activity_kind = .open, } })) orelse return error.TestExpectedEqual;
try std.testing.expectEqual(types.ToolActivityKind.open, started_kind); try std.testing.expectEqual(types.ToolActivityKind.open, runtime.toolActivityRecord(id).?.activity_kind); try std.testing.expectEqual(types.ToolActivityKind.open, runtime.focusedToolActivityKind().?); switch (runtime.activityProjection()) { .tool_slot => |slot| try std.testing.expectEqual(types.ToolActivityKind.open, slot.kind), else => return error.TestExpectedEqual, }}
test "transcript lifecycle terminal and finalization transitions stay batch safe" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const cases = [_]struct { call_id: []const u8, kind: types.ToolOutcomeKind, summary: []const u8, marker_style: []const u8, marker: []const u8, }{ .{ .call_id = "completed", .kind = .completed, .summary = "completed", .marker_style = ui_render.system_notice_text_style, .marker = "●" }, .{ .call_id = "denied", .kind = .denied, .summary = "denied", .marker_style = ui_render.red_style, .marker = "⊘" }, .{ .call_id = "cancelled", .kind = .cancelled, .summary = "cancelled", .marker_style = ui_render.warning_style, .marker = "■" }, .{ .call_id = "failed", .kind = .failed, .summary = "failed", .marker_style = ui_render.red_style, .marker = "●" }, }; for (cases) |case| { const id = lifecycleId(1, case.call_id); const entry_id = try startLifecycle(&runtime, alloc, 1, case.call_id); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = case.kind, .summary = case.summary }, } }); var expected: [128]u8 = undefined; const expected_line = if (case.kind == .cancelled) try std.fmt.bufPrint( &expected, "{s}{s}{s} {s}{s}{s} · What can fx do differently?\n", .{ case.marker_style, case.marker, ui_render.reset_style, ui_render.hint_style, case.summary, ui_render.reset_style, }, ) else try std.fmt.bufPrint( &expected, "{s}{s}{s} {s}\n", .{ case.marker_style, case.marker, ui_render.reset_style, case.summary }, ); try expectRawEntryBytes(&runtime, entry_id, expected_line); } try std.testing.expect(runtime.activityProjection() == .none);
_ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = lifecycleId(2, "old"), .tool_name = "read_file", .activity_kind = .read, } }); const old_entry_id = runtime.toolActivityRecord(lifecycleId(2, "old")).?.entry_id; _ = try startLifecycle(&runtime, alloc, 3, "new");
for ([_]types.ToolLifecycleEvent{ .{ .turn_finished = .{ .turn_id = 2, .outcome = .completed } }, .{ .turn_finished = .{ .turn_id = 2, .outcome = .interrupted } }, .{ .turn_finished = .{ .turn_id = 1, .outcome = .failed } }, }) |event| _ = try runtime.applyToolLifecycle(alloc, event); try expectRawEntryBytes(&runtime, old_entry_id, "● Tool was not executed\n");
_ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(2, "old"), .outcome = .{ .kind = .completed, .summary = "● Late completion" }, } }); var late_completion_expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, old_entry_id, try std.fmt.bufPrint( &late_completion_expected, "{s}●{s} Late completion\n", .{ ui_render.system_notice_text_style, ui_render.reset_style }, )); try std.testing.expectEqual( null, runtime.toolDetailForEntry(old_entry_id).?.fallback_disposition, ); try std.testing.expect(runtime.isLifecyclePinned(old_entry_id));
try runtime.finishLifecycleBatch(alloc); try std.testing.expectEqual(@as(u64, 2), runtime.finalizedToolTurnWatermark()); try std.testing.expect(runtime.toolActivityRecord(lifecycleId(2, "old")) == null); try std.testing.expect(runtime.toolActivityRecord(lifecycleId(3, "new")) != null); try std.testing.expect(!runtime.isLifecyclePinned(old_entry_id));
_ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = lifecycleId(2, "late-start"), .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); try std.testing.expect(runtime.toolActivityRecord(lifecycleId(2, "late-start")) == null);}
test "completed tool status projection keeps lifecycle styling without activity state" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); var metrics: Metrics = .{};
try runtime.writeCompletedToolStatus( alloc, &metrics, .completed, "● Ran pwd\x1b[0m \x1b[38;5;245mpwd\x1b[0m", true, );
try std.testing.expectEqual(@as(usize, 1), runtime.entries.items.len); try std.testing.expectEqual(RawEntryClass.tool_status, runtime.entries.items[0].raw_bytes.class); try expectRawEntryBytes( &runtime, runtime.entries.items[0].raw_bytes.id, "\x1b[38;5;250m●\x1b[0m Ran pwd\x1b[0m \x1b[38;5;245mpwd\x1b[0m\n", ); try std.testing.expect(runtime.activityProjection() == .none); try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount());}
test "transcript lifecycle terminal markers preserve ANSI summaries and normalize markerless failures" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const styled_entry_id = try startLifecycle(&runtime, alloc, 1, "styled"); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(1, "styled"), .outcome = .{ .kind = .completed, .summary = "● Ran command\x1b[0m", }, } }); var styled_expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, styled_entry_id, try std.fmt.bufPrint( &styled_expected, "{s}●{s} Ran command\x1b[0m\n", .{ ui_render.system_notice_text_style, ui_render.reset_style }, ));
const styled_cancelled_entry_id = try startLifecycle(&runtime, alloc, 1, "styled-cancelled"); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(1, "styled-cancelled"), .outcome = .{ .kind = .cancelled, .summary = "● Cancelled\x1b[0m \x1b[38;5;245msleep 30\x1b[0m", }, } }); var styled_cancelled_expected: [256]u8 = undefined; try expectRawEntryBytes(&runtime, styled_cancelled_entry_id, try std.fmt.bufPrint( &styled_cancelled_expected, "{s}■{s}{s} Cancelled\x1b[0m \x1b[38;5;245msleep 30\x1b[0m{s}" ++ " · What can fx do differently?\n", .{ ui_render.warning_style, ui_render.reset_style, ui_render.hint_style, ui_render.reset_style, }, ));
const malformed_entry_id = try startLifecycle(&runtime, alloc, 1, "malformed"); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(1, "malformed"), .outcome = .{ .kind = .failed, .summary = "write_file failed: invalid JSON arguments", }, } }); var malformed_expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, malformed_entry_id, try std.fmt.bufPrint( &malformed_expected, "{s}●{s} write_file failed: invalid JSON arguments\n", .{ ui_render.red_style, ui_render.reset_style }, ));
const cancelled_entry_id = try startLifecycle(&runtime, alloc, 2, "cancelled"); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 2, .outcome = .interrupted, } }); var cancelled_expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, cancelled_entry_id, try std.fmt.bufPrint( &cancelled_expected, "{s}■{s} {s}Tool cancelled{s} · What can fx do differently?\n", .{ ui_render.warning_style, ui_render.reset_style, ui_render.hint_style, ui_render.reset_style, }, ));
const failed_entry_id = try startLifecycle(&runtime, alloc, 3, "failed"); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 3, .outcome = .failed, } }); var failed_expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, failed_entry_id, try std.fmt.bufPrint( &failed_expected, "{s}●{s} Tool failed\n", .{ ui_render.red_style, ui_render.reset_style }, ));}
test "active tool cancellation is presented immediately without closing lifecycle state" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const ids = [_]types.ToolLifecycleId{ lifecycleId(1, "read"), lifecycleId(1, "command"), }; for (ids) |id| { _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = if (std.mem.eql(u8, id.call_id, "read")) "read_file" else "run_command", .activity_kind = if (std.mem.eql(u8, id.call_id, "read")) .read else .command, } }); }
try std.testing.expect(try runtime.presentActiveToolCancellation(alloc)); try std.testing.expectEqual(@as(usize, 2), runtime.activeToolActivityCount());
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expectEqual( @as(usize, 2), std.mem.count(u8, rendered.bytes, "What can fx do differently?"), ); try std.testing.expect(std.mem.find(u8, rendered.bytes, "System:") == null); try std.testing.expect(std.mem.find(u8, rendered.bytes, "Cancelling") == null);
_ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = ids[1], .outcome = .{ .kind = .cancelled, .summary = "Cancelled sleep 30" }, } }); try std.testing.expectEqual(@as(usize, 1), runtime.activeToolActivityCount());}
test "late successful settlement preserves its result and one turn cancellation" { const alloc = std.testing.allocator; for ([_]bool{ false, true }) |finish_before_terminal| { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, "late-success"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } });
try std.testing.expect(try runtime.presentActiveToolCancellation(alloc)); if (finish_before_terminal) { _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .interrupted, } }); } _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Read the file" }, .result = "late result", } }); if (!finish_before_terminal) { _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .interrupted, } }); }
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "What can fx do differently?"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "Read the file"), ); try std.testing.expectEqual( RawEntryClass.turn_cancellation, runtime.entries.getLast().raw_bytes.class, ); const detail = runtime.toolDetailForEntry(runtime.toolActivityRecord(id).?.entry_id).?; try std.testing.expectEqualStrings("late result", detail.result.?); try std.testing.expectEqual(types.ToolOutcomeKind.completed, detail.outcome.?); }}
test "post-cancel sibling settlement preserves one turn cancellation" { const alloc = std.testing.allocator; for ([_]bool{ false, true }) |sibling_reports_terminal| { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const first = lifecycleId(1, "first"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = first, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); try std.testing.expect(try runtime.presentActiveToolCancellation(alloc)); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = first, .outcome = .{ .kind = .completed, .summary = "Read first" }, } });
const sibling = lifecycleId(1, "sibling"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = sibling, .reconciles_provisional_call_id = null, .tool_name = "grep_files", .activity_kind = .read, } }); if (sibling_reports_terminal) { _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = sibling, .outcome = .{ .kind = .cancelled, .summary = "Search cancelled" }, } }); } _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = first.turn_id, .outcome = .interrupted, } });
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "What can fx do differently?"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "Read first"), ); }}
test "late zero-output command settlement reserves distinct presentation entries" { const alloc = std.testing.allocator; for ([_]bool{ false, true }) |finish_before_terminal| { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, "zero-output-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "shell", .activity_kind = .command, .arguments_json = "{\"request\":{\"action\":\"run\",\"command\":\"true\"}}", } }); try std.testing.expect(try runtime.presentActiveToolCancellation(alloc)); if (finish_before_terminal) { _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .interrupted, } }); } _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran true" }, .result = "exit_code=0\n", .result_memory = .{ .command_process_presentation = .{ .exit_code = 0 }, }, } }); if (!finish_before_terminal) { _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .interrupted, } }); }
var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "What can fx do differently?"), ); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "Ran \x1b[38;5;245m\x1b[38;5;252mtrue\x1b[39m"), ); const detail = runtime.toolDetailForEntry(runtime.toolActivityRecord(id).?.entry_id).?; try std.testing.expectEqual(types.ToolOutcomeKind.completed, detail.outcome.?); try std.testing.expectEqual( types.CommandProcessPresentation{ .exit_code = 0 }, detail.command_process_presentation.?, ); try std.testing.expectEqual( RawEntryClass.command_output, runtime.rawEntryClass(detail.command_output_entry_id.?).?, ); for (runtime.entries.items, 0..) |entry, entry_index| { for (runtime.entries.items[entry_index + 1 ..]) |other| { try std.testing.expect(entry.id() != other.id()); } } }}
fn checkLateZeroOutputCommandCancellationAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc);
const id = lifecycleId(1, "allocation-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "shell", .activity_kind = .command, .arguments_json = "{\"request\":{\"action\":\"run\",\"command\":\"true\"}}", } }); try std.testing.expect(try runtime.presentActiveToolCancellation(alloc)); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .interrupted, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran true" }, .result = "exit_code=0\n", .result_memory = .{ .command_process_presentation = .{ .exit_code = 0 }, }, } });
const detail = runtime.toolDetailForEntry(runtime.toolActivityRecord(id).?.entry_id).?; try std.testing.expectEqual(types.ToolOutcomeKind.completed, detail.outcome.?); try std.testing.expectEqual( RawEntryClass.command_output, runtime.rawEntryClass(detail.command_output_entry_id.?).?, ); var rendered = try runtime.prepareTranscriptSource(alloc, null); defer rendered.deinit(alloc); try std.testing.expectEqual( @as(usize, 1), std.mem.count(u8, rendered.bytes, "What can fx do differently?"), );}
fn checkLateZeroOutputCommandCancellationAllocationFailures(alloc: Allocator) !void { return checkLateZeroOutputCommandCancellationAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "late zero-output command cancellation remains atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkLateZeroOutputCommandCancellationAllocationFailures, .{}, );}
fn expectRawEntryBytes( runtime: *const TranscriptRuntime, entry_id: u32, expected: []const u8,) !void { for (runtime.entries.items) |entry| { if (entry == .raw_bytes and entry.raw_bytes.id == entry_id) { try std.testing.expectEqualStrings(expected, entry.raw_bytes.bytes); return; } } return error.TestExpectedEqual;}
fn transcriptContainsEntry(runtime: *const TranscriptRuntime, entry_id: u32) bool { for (runtime.entries.items) |entry| { if (entry.id() == entry_id) return true; } return false;}
fn checkLifecycleRepositionAllocationFailuresImpl(alloc: Allocator) !void { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "ALLOCATION TRANSCRIPT 01\n" ++ "ALLOCATION TRANSCRIPT 02\n" ++ "ALLOCATION TRANSCRIPT 03\n" ++ "ALLOCATION TRANSCRIPT 04\n" ++ "ALLOCATION TRANSCRIPT 05\n" ++ "ALLOCATION TRANSCRIPT 06\n", .subagent_status, ); var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, );
const id = lifecycleId(93, "allocation-command"); _ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "run_command", .activity_kind = .command, } }); _ = try runtime.appendSemanticNotice(alloc, .{ .topic = "permissions", .tone = .success, .body = "Auto agent approved this request: Running command.", .visibility = .full_only, });
var source_before = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_before.deinit(std.testing.allocator); const diagnostic_before = runtime.transcriptCommitDiagnostic(); _ = runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"seq 1 1\"}", .place_after_current_transcript = true, } }) catch |err| { var source_after = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer source_after.deinit(std.testing.allocator); try std.testing.expectEqualStrings(source_before.bytes, source_after.bytes); try std.testing.expectEqualDeep( diagnostic_before, runtime.transcriptCommitDiagnostic(), ); try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .provisional); return err; };
try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .authoritative); try std.testing.expectEqual( transcript_runtime.TranscriptCommitDiagnosticState.stable, runtime.transcriptCommitDiagnostic().state, );}
fn checkLifecycleRepositionAllocationFailures(alloc: Allocator) !void { return checkLifecycleRepositionAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "coalesced approval lifecycle reposition is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkLifecycleRepositionAllocationFailures, .{}, );}
test "lifecycle pin cleanup preserves a capped canonical anchor without pruning" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, .max_transcript_bytes = 64, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const long_tail = try makeLongPastePrompt(alloc, 40); defer alloc.free(long_tail); _ = try runtime.appendRawTranscriptEntryClassified( alloc, long_tail, .subagent_status, ); const id = lifecycleId(94, "cleanup-command"); _ = try startLifecycle(&runtime, alloc, id.turn_id, id.call_id); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .completed, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Cleanup complete" }, } });
var metrics = Metrics{}; var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try std.testing.expect(committed_source.recorded_entries_authoritative); try std.testing.expect(committed_source.bytes.len > runtime.transcript.items.len); var committed_prepared = try runtime.prepareTranscriptSurfacePaintForArea( alloc, &metrics, .{ .top = runtime.owned_top_row, .bottom = runtime.layout.content_bottom }, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); const committed_diagnostic = runtime.transcriptCommitDiagnostic();
try runtime.finishLifecycleBatch(alloc);
try std.testing.expectEqualDeep( committed_diagnostic, runtime.transcriptCommitDiagnostic(), ); try std.testing.expectEqual(@as(usize, 0), runtime.lifecyclePinCount()); try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount()); var cleaned_source = try runtime.prepareTranscriptSource(alloc, null); defer cleaned_source.deinit(alloc); try std.testing.expectEqualStrings(committed_source.bytes, cleaned_source.bytes);}
test "lifecycle pin cleanup preserves a canonical anchor when retention prunes" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(48, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc); try runtime.enableShadowVt(alloc);
const anchored = "anchored lifecycle cleanup transcript\n"; _ = try runtime.appendRawTranscriptEntryClassified( alloc, anchored, .subagent_status, ); const id = lifecycleId(95, "pruning-cleanup-command"); _ = try startLifecycle(&runtime, alloc, id.turn_id, id.call_id); _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = id.turn_id, .outcome = .completed, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Cleanup complete" }, } });
var committed_source = try runtime.prepareTranscriptSource(alloc, null); defer committed_source.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_source.bytes, testSelection(0), 0, 4, 1, 1, ); const committed_history_visual_offset = try stableHistoryVisualOffsetForTest(&runtime); runtime.max_retained_transcript_bytes = transcript_store.retainedStructuredBytes(&runtime) - anchored.len;
try runtime.finishLifecycleBatch(alloc);
try expectStableNormalBufferRecoveryForTest( &runtime, committed_history_visual_offset, ); try std.testing.expectEqual(@as(usize, 0), runtime.lifecyclePinCount()); try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount()); var retained_source = try runtime.prepareTranscriptSource(alloc, null); defer retained_source.deinit(alloc); try std.testing.expectEqual( @as(usize, 0), std.mem.count(u8, retained_source.bytes, anchored), );}
fn checkLifecycleAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(64); defer runtime.deinit(alloc); const existing = try runtime.appendRawTranscriptEntryClassified( alloc, "existing transcript state\n", .subagent_status, ); const entry_id = startLifecycle(&runtime, alloc, 1, "transaction") catch |err| { try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount()); try std.testing.expectEqual(@as(usize, 0), runtime.lifecyclePinCount()); try expectRawEntryBytes(&runtime, existing, "existing transcript state\n"); return err; }; _ = runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = lifecycleId(1, "transaction"), .text = "● Reading updated", } }) catch |err| { try std.testing.expect(runtime.isLifecyclePinned(entry_id)); try std.testing.expect(std.mem.find( u8, runtime.toolStatusEntryLabel(entry_id).?, "updated", ) == null); return err; }; _ = runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed }, }) catch |err| { try expectRawEntryBytes(&runtime, entry_id, "● Reading updated\n"); try std.testing.expect(runtime.isLifecyclePinned(entry_id)); return err; }; runtime.finishLifecycleBatch(alloc) catch |err| { try std.testing.expect(runtime.toolActivityRecord(lifecycleId(1, "transaction")) != null); try std.testing.expect(runtime.isLifecyclePinned(entry_id)); return err; }; try std.testing.expect(runtime.toolActivityRecord(lifecycleId(1, "transaction")) == null); try std.testing.expect(!runtime.isLifecyclePinned(entry_id));}
fn checkLifecycleAllocationFailures(alloc: Allocator) !void { return checkLifecycleAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "transcript lifecycle transactions are atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkLifecycleAllocationFailures, .{}, );}
fn checkLateTerminalFallbackAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); const id = lifecycleId(1, "late-terminal");
_ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "read_file", .activity_kind = .read, } }); const entry_id = runtime.toolActivityRecord(id).?.entry_id; _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed, } });
_ = runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Late completion" }, .result = "late result", } }) catch |err| { try expectRawEntryBytes(&runtime, entry_id, "● Tool was not executed\n"); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expect(detail.result == null); try std.testing.expectEqual( transcript_blocks.ToolFallbackDisposition.not_executed, detail.fallback_disposition.?, ); var compact = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer compact.deinit(std.testing.allocator); try std.testing.expect(std.mem.find(u8, compact.bytes, "1 not executed") != null); return err; };
var expected: [128]u8 = undefined; try expectRawEntryBytes(&runtime, entry_id, try std.fmt.bufPrint( &expected, "{s}●{s} Late completion\n", .{ ui_render.system_notice_text_style, ui_render.reset_style }, )); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expectEqualStrings("late result", detail.result.?); try std.testing.expect(detail.fallback_disposition == null); var compact = try runtime.prepareTranscriptSource(std.testing.allocator, null); defer compact.deinit(std.testing.allocator); try std.testing.expect(std.mem.find(u8, compact.bytes, "1 not executed") == null); try std.testing.expect(std.mem.find(u8, compact.bytes, "Late completion") != null);}
fn checkLateTerminalFallbackAllocationFailures(alloc: Allocator) !void { return checkLateTerminalFallbackAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "late terminal fallback replacement is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkLateTerminalFallbackAllocationFailures, .{}, );}
fn checkProvisionalTerminalAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); const id = lifecycleId(1, "provisional-terminal");
_ = try runtime.applyToolLifecycle(alloc, .{ .provisional = .{ .id = id, .tool_name = "read_file", .activity_kind = .read, } }); const entry_id = runtime.toolActivityRecord(id).?.entry_id; const entries_before = runtime.entries.items.len; const next_entry_id_before = runtime.next_entry_id;
_ = runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Read provisional file" }, .result = "provisional result", } }) catch |err| { try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .provisional); try std.testing.expectEqual(entries_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expectEqualStrings("read_file", detail.tool_name); try std.testing.expectEqual(types.ToolActivityKind.read, detail.activity_kind.?); try std.testing.expect(detail.result == null); try std.testing.expect(detail.outcome == null); const status = runtime.toolStatusEntryLabel(entry_id).?; try std.testing.expect(std.mem.find(u8, status, "read_file") != null); try std.testing.expect(std.mem.find(u8, status, "Read provisional file") == null); return err; };
try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .terminal); try std.testing.expectEqual(entries_before, runtime.entries.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expectEqualStrings("read_file", detail.tool_name); try std.testing.expectEqualStrings("provisional result", detail.result.?); try std.testing.expectEqual(types.ToolOutcomeKind.completed, detail.outcome.?);}
fn checkProvisionalTerminalAllocationFailures(alloc: Allocator) !void { return checkProvisionalTerminalAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "provisional terminal admission is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkProvisionalTerminalAllocationFailures, .{}, );}
fn checkCommandProcessTerminalAllocationFailuresImpl(alloc: Allocator) !void { var runtime = lifecycleTestRuntime(null); defer runtime.deinit(alloc); const id = lifecycleId(1, "command-process-terminal");
_ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command, .arguments_json = "{\"command\":\"true\"}", } }); const entry_id = runtime.toolActivityRecord(id).?.entry_id; const entries_before = runtime.entries.items.len; const blocks_before = runtime.command_output_blocks.items.len; const next_entry_id_before = runtime.next_entry_id;
_ = runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran true" }, .result_memory = .{ .command_process_presentation = .{ .exit_code = 0 } }, } }) catch |err| { try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .authoritative); try std.testing.expectEqual(entries_before, runtime.entries.items.len); try std.testing.expectEqual(blocks_before, runtime.command_output_blocks.items.len); try std.testing.expectEqual(next_entry_id_before, runtime.next_entry_id); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expect(detail.command_process_presentation == null); try std.testing.expect(detail.command_output_entry_id == null); const status = runtime.toolStatusEntryLabel(entry_id).?; try std.testing.expect(std.mem.find(u8, status, "run_command") != null); try std.testing.expect(std.mem.find(u8, status, "\x1b[38;5;250mtrue\x1b[39m") == null); return err; };
try std.testing.expect(runtime.toolActivityRecord(id).?.phase == .terminal); try std.testing.expectEqual(entries_before + 1, runtime.entries.items.len); try std.testing.expectEqual(blocks_before + 1, runtime.command_output_blocks.items.len); try std.testing.expectEqual(next_entry_id_before + 1, runtime.next_entry_id); const detail = runtime.toolDetailForEntry(entry_id).?; try std.testing.expectEqual( types.CommandProcessPresentation{ .exit_code = 0 }, detail.command_process_presentation.?, ); try std.testing.expectEqual(next_entry_id_before, detail.command_output_entry_id.?); try std.testing.expectEqual(next_entry_id_before, runtime.command_output_blocks.items[0].entry_id.?);}
fn checkCommandProcessTerminalAllocationFailures(alloc: Allocator) !void { return checkCommandProcessTerminalAllocationFailuresImpl(alloc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => err, };}
test "command process terminal admission is atomic across allocation failures" { try std.testing.checkAllAllocationFailures( std.testing.allocator, checkCommandProcessTerminalAllocationFailures, .{}, );}
test "capped parallel lifecycle progress keeps bounded allocations and pins" { const backing = std.testing.allocator; var counted = std.testing.FailingAllocator.init(backing, .{}); const alloc = counted.allocator(); var runtime = lifecycleTestRuntime(256); defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntryClassified( alloc, "unrelated output retained near the configured transcript cap\n", .subagent_status, ); const first_entry_id = try startLifecycle(&runtime, alloc, 1, call_ids[0]); const second_entry_id = try startLifecycle(&runtime, alloc, 1, call_ids[1]); try std.testing.expectEqual(@as(usize, 2), runtime.tool_details.items.len); try std.testing.expect(runtime.toolDetailForEntry(first_entry_id) != null); try std.testing.expect(runtime.toolDetailForEntry(second_entry_id) != null); const capacity_after_start = runtime.tool_details.capacity; const progress_text = "● Reading progress " ++ ("x" ** 256); _ = try runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = lifecycleId(1, call_ids[0]), .text = progress_text, } }); try std.testing.expectEqual(@as(usize, 2), runtime.tool_details.items.len); try std.testing.expectEqual(capacity_after_start, runtime.tool_details.capacity);
var allocations_before_progress = counted.allocations; for (0..120) |index| { if (index == 20) allocations_before_progress = counted.allocations; _ = try runtime.applyToolLifecycle(alloc, .{ .progress = .{ .id = lifecycleId(1, call_ids[index % call_ids.len]), .text = "● Reading progress", } }); try std.testing.expectEqual(capacity_after_start, runtime.tool_details.capacity); } try std.testing.expectEqual(@as(usize, 2), runtime.tool_details.items.len); try std.testing.expect(runtime.toolDetailForEntry(first_entry_id) != null); try std.testing.expect(runtime.toolDetailForEntry(second_entry_id) != null); try std.testing.expect(counted.allocations - allocations_before_progress <= 600); try std.testing.expectEqual(@as(usize, 2), runtime.lifecyclePinCount());}
const call_ids = [_][]const u8{ "first", "second" };
test "lifecycle pins survive low cap until batch cleanup restores prune eligibility" { const alloc = std.testing.allocator; var runtime = lifecycleTestRuntime(48); defer runtime.deinit(alloc);
const first = try startLifecycle(&runtime, alloc, 1, call_ids[0]); const second = try startLifecycle(&runtime, alloc, 1, call_ids[1]); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "ordinary output that creates retention pressure\n", .subagent_status, );
try std.testing.expectEqual(@as(usize, 2), runtime.lifecyclePinCount()); try std.testing.expect(transcriptContainsEntry(&runtime, first)); try std.testing.expect(transcriptContainsEntry(&runtime, second));
_ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = lifecycleId(1, "first"), .outcome = .{ .kind = .completed, .summary = "● First complete" }, } }); try std.testing.expectEqual(@as(usize, 2), runtime.lifecyclePinCount()); var first_complete_expected: [128]u8 = undefined; try std.testing.expectEqualStrings( try std.fmt.bufPrint( &first_complete_expected, "{s}●{s} First complete\n", .{ ui_render.system_notice_text_style, ui_render.reset_style }, ), runtime.entries.items[0].raw_bytes.bytes, );
try runtime.finishLifecycleBatch(alloc); _ = try runtime.appendRawTranscriptEntryClassified( alloc, "next ordinary retention pass\n", .subagent_status, );
try std.testing.expectEqual(@as(usize, 0), runtime.lifecyclePinCount()); try std.testing.expectEqual(@as(usize, 0), runtime.toolActivityRecordCount()); try std.testing.expect( !transcriptContainsEntry(&runtime, first) or !transcriptContainsEntry(&runtime, second), );}
test "streaming assistant finality releases complete rows and holds only the partial tail" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var metrics: Metrics = .{}; _ = try runtime.streamAssistantChunk( alloc, &metrics, "first finalized row\nsecond finalized row\npartial tail", );
var partial_source = try runtime.prepareTranscriptSource(alloc, null); defer partial_source.deinit(alloc); const partial_start = std.mem.find(u8, partial_source.bytes, " partial tail") orelse return error.TestExpectedPartialAssistantTail; try std.testing.expectEqual( @as(?usize, partial_start), runtime.transcript_release.finality_floor( partial_source.finality, runtime.finalizedToolTurnWatermark(), null, ), );
_ = try runtime.streamAssistantChunk(alloc, &metrics, "\n"); var completed_source = try runtime.prepareTranscriptSource(alloc, null); defer completed_source.deinit(alloc); try std.testing.expectEqual( @as(?usize, completed_source.bytes.len), runtime.transcript_release.finality_floor( completed_source.finality, runtime.finalizedToolTurnWatermark(), null, ), );}
test "blank streaming assistant tail keeps finality inside the prepared source" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntry(alloc, "stable history\n"); var metrics: Metrics = .{}; _ = try runtime.streamAssistantChunk(alloc, &metrics, " ");
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual( @as(?usize, source.bytes.len), runtime.transcript_release.finality_floor( source.finality, runtime.finalizedToolTurnWatermark(), null, ), );}
test "empty assistant tail leaves the complete prepared source final" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try runtime.appendRawTranscriptEntry(alloc, "stable history\n"); _ = try runtime.appendAssistantTurnEntry(alloc);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual( @as(?usize, source.bytes.len), runtime.transcript_release.finality_floor( source.finality, runtime.finalizedToolTurnWatermark(), null, ), );}
test "native history defers partial assistant paints but publishes hard lines" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(40, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, "history\n", testSelection(1), 1, 4, 1, 1, ); var metrics: Metrics = .{}; _ = try runtime.streamAssistantChunk(alloc, &metrics, "partial"); try std.testing.expect(!runtime.render_requests.hasReason(.transcript));
_ = try runtime.streamAssistantChunk(alloc, &metrics, " line\n"); try std.testing.expect(runtime.render_requests.hasReason(.transcript));}
test "streaming assistant finality keeps soft wraps of a partial hard line mutable" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(12, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var metrics: Metrics = .{}; _ = try runtime.streamAssistantChunk( alloc, &metrics, "finalized row\npartial tail wraps across several visual rows", );
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); const partial_start = std.mem.find(u8, source.bytes, " partial") orelse return error.TestExpectedWrappedPartialAssistantTail; try std.testing.expectEqual( @as(?usize, partial_start), runtime.transcript_release.finality_floor( source.finality, runtime.finalizedToolTurnWatermark(), null, ), );}
test "finality floor holds unfenced tool turn across quiet ticks and settles after the fence" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(60, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
for (0..6) |index| { var line: [32]u8 = undefined; const text = try std.fmt.bufPrint(&line, "final context {d:0>2}\n", .{index}); _ = try runtime.appendRawTranscriptEntry(alloc, text); } var base_source = try runtime.prepareTranscriptSource(alloc, null); defer base_source.deinit(alloc); var base_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &base_source); defer base_prepared.deinit(alloc); try commitPreparedForTest(&runtime, alloc, &base_source, &base_prepared);
var turn_call_ids: [8][12]u8 = undefined; for (0..8) |index| { const call_id = try std.fmt.bufPrint(&turn_call_ids[index], "t2-call-{d:0>2}", .{index}); const id = types.ToolLifecycleId{ .turn_id = 2, .call_id = call_id }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .presentation_group_id = .{ .turn_id = 2, .anchor_step_id = 1 }, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Read one file" }, } }); } try runtime.finishLifecycleBatch(alloc); try std.testing.expectEqual(@as(u64, 0), runtime.finalizedToolTurnWatermark());
var live_source = try runtime.prepareTranscriptSource(alloc, null); defer live_source.deinit(alloc); var live_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &live_source); defer live_prepared.deinit(alloc); try commitPreparedForTest(&runtime, alloc, &live_source, &live_prepared);
const held = runtime.transcript_commit_state.stable; try std.testing.expect(held.visual_offset > held.history_visual_offset);
// Quiet ticks over the unchanged flow release nothing: byte stability // is not finality while the turn is unfenced. var quiet_source = try runtime.prepareTranscriptSource(alloc, null); defer quiet_source.deinit(alloc); var quiet_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &quiet_source); defer quiet_prepared.deinit(alloc); const quiet_facts = runtime.planTranscriptScroll(&quiet_prepared); try std.testing.expect(quiet_facts.source_compatible); try std.testing.expectEqual(@as(u32, 0), quiet_facts.semantic_rows); try std.testing.expect(quiet_facts.finality_hold);
// A later mutation of the same group still releases nothing. const late_call = types.ToolLifecycleId{ .turn_id = 2, .call_id = "t2-late" }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = late_call, .presentation_group_id = .{ .turn_id = 2, .anchor_step_id = 1 }, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = late_call, .outcome = .{ .kind = .completed, .summary = "Read late file" }, } }); try runtime.finishLifecycleBatch(alloc); var mutated_source = try runtime.prepareTranscriptSource(alloc, null); defer mutated_source.deinit(alloc); var mutated_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &mutated_source); defer mutated_prepared.deinit(alloc); const mutated_facts = runtime.planTranscriptScroll(&mutated_prepared); try std.testing.expectEqual(@as(u32, 0), mutated_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), mutated_facts.planned_rows); try commitPreparedForTest(&runtime, alloc, &mutated_source, &mutated_prepared);
// The fence and the final mutation arrive in one batch: the first // post-fence frame is byte-incompatible and must hold (zero release), // re-anchoring on the final flow. _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 2, .outcome = .completed, } }); try runtime.finishLifecycleBatch(alloc); try std.testing.expectEqual(@as(u64, 2), runtime.finalizedToolTurnWatermark());
// The debt settles on the first eligible frame after the fence: the // same frame when the fence publication changed no bytes, or the frame // after a byte-incompatible publication re-anchors (Phase A then B). var released: u32 = 0; for (0..2) |frame_index| { var frame_source = try runtime.prepareTranscriptSource(alloc, null); defer frame_source.deinit(alloc); var frame_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &frame_source); defer frame_prepared.deinit(alloc); const frame_facts = runtime.planTranscriptScroll(&frame_prepared); if (!frame_facts.source_compatible) { // Phase A: zero release while the anchor is stale. try std.testing.expectEqual(@as(u32, 0), frame_facts.semantic_rows); try std.testing.expectEqual(@as(u16, 0), frame_facts.planned_rows); try std.testing.expect(frame_facts.finality_hold); try std.testing.expectEqual(@as(usize, 0), frame_index); } released += frame_facts.semantic_rows; try commitPreparedForTest(&runtime, alloc, &frame_source, &frame_prepared); } try std.testing.expect(released > 0); const settled = runtime.transcript_commit_state.stable; try std.testing.expectEqual(settled.visual_offset, settled.history_visual_offset);}
test "catch-up release materializes history before staging a larger finality hold" { const alloc = std.testing.allocator; const committed_flow = "row-00\nrow-01\nrow-02\nrow-03\n" ++ "row-04\nrow-05\nrow-06\nrow-07\n"; const target_flow = committed_flow ++ "row-08\nrow-09\nrow-10\nrow-11\nrow-12\nrow-13\n"; const finality_floor = "row-00\nrow-01\nrow-02\n".len; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var committed_source = try testSource(alloc, committed_flow, runtime.layout.cols); defer committed_source.deinit(alloc); var committed_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &committed_source, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_flow, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); switch (runtime.transcript_commit_state) { .stable => |*anchor| { anchor.history_visual_offset = 0; anchor.history_catchup_pending = true; }, .invalid, .recovering => return error.TestExpectedStableTranscript, }
var source = try testSource(alloc, target_flow, runtime.layout.cols); defer source.deinit(alloc); source.finality.mutation_pin_start = finality_floor; var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u32, 3), facts.semantic_rows); try std.testing.expect(facts.semantic_rows > 0); try std.testing.expect( facts.target_visual_offset - facts.source_visual_offset > facts.semantic_rows, ); try std.testing.expect(facts.finality_hold);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqualStrings( "row-00\r\nrow-01\r\nrow-02\r\n", transition.document_append.bytes, ); switch (transition.document_append.clear) { .rows => |rows| try std.testing.expectEqual(@as(u16, 3), rows), .remainder => return error.TestExpectedHistoryReplay, }}
test "partial finality release bounds append before staging withheld viewport" { const alloc = std.testing.allocator; const committed_flow = "row-00\nrow-01\nrow-02\nrow-03\n" ++ "row-04\nrow-05\nrow-06\nrow-07\n"; const target_flow = committed_flow ++ "row-08\nrow-09\nrow-10\nrow-11\nrow-12\nrow-13\n"; const finality_floor = "row-00\nrow-01\nrow-02\nrow-03\nrow-04\n".len; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
var committed_source = try testSource(alloc, committed_flow, runtime.layout.cols); defer committed_source.deinit(alloc); var committed_prepared = try prepareTestSourceForCurrentArea( &runtime, alloc, &committed_source, ); defer committed_prepared.deinit(alloc); try installStableAnchorForTest( &runtime, alloc, committed_flow, committed_prepared.selection, preparedVisualOffsetForTest(&committed_prepared), committed_prepared.cursor.cursor_row, committed_prepared.cursor.cursor_col, 1, ); switch (runtime.transcript_commit_state) { .stable => |*anchor| anchor.total_visual_rows = 8, .invalid, .recovering => return error.TestExpectedStableTranscript, }
var source = try testSource(alloc, target_flow, runtime.layout.cols); defer source.deinit(alloc); source.finality.mutation_pin_start = finality_floor; var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc); const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expectEqual(@as(u32, 4), facts.source_visual_offset); try std.testing.expectEqual(@as(u32, 10), facts.target_visual_offset); try std.testing.expectEqual(@as(u32, 1), facts.semantic_rows); try std.testing.expectEqual(@as(u16, 1), facts.planned_rows); try std.testing.expect(facts.finality_hold);
const scroll_plan = render_engine.frame_scroll_plan.merge( runtime.layout.rows, runtime.owned_top_row, 0, facts.planned_rows, ); var plan = testPaintPlan(&runtime, prepared.selection); var transition = try resolveAndSealTranscriptTransitionForTest( &runtime, alloc, &source, &prepared, render_engine.frame_layout.CommittedLayoutSnapshot.fromPaintPlan(plan), &plan, scroll_plan, ); defer transition.deinit(alloc);
try std.testing.expectEqualStrings( "row-08\r\n", transition.document_append.bytes, ); try std.testing.expectEqual(@as(u32, 10), transition.visual_offset); try std.testing.expectEqual(@as(u32, 5), transition.history_visual_offset);}
test "recovery growth does not release rows past the finality floor" { const alloc = std.testing.allocator; const attempt_flow = "row-00\nrow-01\nrow-02\nrow-03\n" ++ "row-04\nrow-05\nrow-06\nrow-07\n"; const grown_flow = attempt_flow ++ "row-08\nrow-09\nrow-10\nrow-11\n"; const finality_floor = "row-00\nrow-01\nrow-02\nrow-03\n".len; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(20, 8, 4), .owned_top_row = 1, .transcript_commit_state = .{ .recovering = .{ .accepted_visual_offset = 4, .accepted_history_visual_offset = 4, .attempt_visual_offset = 4, .attempt_total_visual_rows = 8, .materialized_visual_offset = 4, .materialized_visual_rows = 4, .materialized_flow_len = attempt_flow.len, .attempt_cols = 20, .tracks_semantic_progress = true, .flow = try alloc.dupe(u8, attempt_flow), .cursor_row = 4, .cursor_col = 1, } }, }; defer runtime.deinit(alloc);
var source = try testSource(alloc, grown_flow, runtime.layout.cols); defer source.deinit(alloc); source.finality.mutation_pin_start = finality_floor; var prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &source); defer prepared.deinit(alloc);
const facts = runtime.planTranscriptScroll(&prepared); try std.testing.expect(facts.source_compatible); try std.testing.expect(facts.recovery_progress_compatible); try std.testing.expectEqual(@as(u32, 8), facts.target_visual_offset); try std.testing.expectEqual(@as(u32, 0), facts.semantic_rows); try std.testing.expectEqual(@as(u32, 0), facts.semantic_progress_rows); try std.testing.expectEqual(@as(u16, 0), facts.planned_rows);}
test "pending replacement notice holds release until the finished replacement settles" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(24, 10, 4), .owned_top_row = 1, }; defer runtime.deinit(alloc);
for (0..4) |index| { var line: [24]u8 = undefined; const text = try std.fmt.bufPrint(&line, "history {d:0>2}\n", .{index}); _ = try runtime.appendRawTranscriptEntry(alloc, text); } var base_source = try runtime.prepareTranscriptSource(alloc, null); defer base_source.deinit(alloc); var base_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &base_source); defer base_prepared.deinit(alloc); try commitPreparedForTest(&runtime, alloc, &base_source, &base_prepared); const notice_id = try runtime.appendReplaceableSemanticNotice(alloc, .{ .topic = "feedback", .tone = .neutral, .body = "Preparing feedback report while external work is blocking", }); for (0..6) |index| { var line: [24]u8 = undefined; const text = try std.fmt.bufPrint(&line, "later {d:0>2}\n", .{index}); _ = try runtime.appendRawTranscriptEntry(alloc, text); }
var live_source = try runtime.prepareTranscriptSource(alloc, null); defer live_source.deinit(alloc); var live_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &live_source); defer live_prepared.deinit(alloc); try commitPreparedForTest(&runtime, alloc, &live_source, &live_prepared); const held = runtime.transcript_commit_state.stable; try std.testing.expect(held.visual_offset > held.history_visual_offset);
// Quiet frames while the producer blocks release nothing. var quiet_source = try runtime.prepareTranscriptSource(alloc, null); defer quiet_source.deinit(alloc); var quiet_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &quiet_source); defer quiet_prepared.deinit(alloc); const quiet_facts = runtime.planTranscriptScroll(&quiet_prepared); try std.testing.expect(quiet_facts.source_compatible); try std.testing.expectEqual(@as(u32, 0), quiet_facts.semantic_rows); try std.testing.expect(quiet_facts.finality_hold);
// Finishing clears the pin and rebases its replacement before planning. // Compatibility permits release; the frame receipt must still accept it. try std.testing.expect(try runtime.replaceSemanticNotice(alloc, notice_id, .{ .topic = "feedback", .tone = .success, .body = "Feedback report ready", })); var replaced_source = try runtime.prepareTranscriptSource(alloc, null); defer replaced_source.deinit(alloc); var replaced_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &replaced_source); defer replaced_prepared.deinit(alloc); const replaced_facts = runtime.planTranscriptScroll(&replaced_prepared); try std.testing.expect(replaced_source.finality.mutation_pin_start == null); try std.testing.expect(replaced_facts.source_compatible); try std.testing.expect(replaced_facts.semantic_rows > 0); try std.testing.expect(replaced_facts.planned_rows > 0); const before_release = runtime.transcriptCommitDiagnostic().history_visual_offset; try commitPreparedForTest(&runtime, alloc, &replaced_source, &replaced_prepared); try std.testing.expectEqual(@min(replaced_facts.semantic_rows, @as(u32, replaced_facts.planned_rows)), runtime.transcriptCommitDiagnostic().history_visual_offset - before_release); try std.testing.expectEqual(replaced_facts.source_visual_offset + @min(replaced_facts.semantic_progress_rows, @as(u32, replaced_facts.planned_rows)), runtime.transcriptCommitDiagnostic().visual_offset);
var settle_source = try runtime.prepareTranscriptSource(alloc, null); defer settle_source.deinit(alloc); try std.testing.expect( std.mem.find(u8, settle_source.bytes, "report ready") != null, ); var settle_prepared = try prepareTestSourceForCurrentArea(&runtime, alloc, &settle_source); defer settle_prepared.deinit(alloc); const settle_facts = runtime.planTranscriptScroll(&settle_prepared); try std.testing.expect(settle_facts.source_compatible); try std.testing.expect(settle_facts.semantic_rows > 0); try commitPreparedForTest(&runtime, alloc, &settle_source, &settle_prepared); const settled = runtime.transcript_commit_state.stable; try std.testing.expectEqual(settled.visual_offset, settled.history_visual_offset);}
fn appendRecordedWriteForFinalityTest( runtime: *TranscriptRuntime, alloc: Allocator, lifecycle_id: ?types.ToolLifecycleId,) !u32 { const entry_id = try runtime.appendRawTranscriptEntryClassified(alloc, "● Wrote receipt.txt\n", .tool_status); try runtime.attachHistoricalToolDetailWithLifecycle( alloc, entry_id, .{ .id = "saved-write", .name = "write_file", .arguments_json = "{\"path\":\"receipt.txt\"}" }, .write, .{ .tool_call_id = @constCast("saved-write"), .tool_name = @constCast("write_file"), .status = .success, .output = @constCast("saved result"), .output_bytes = 12, .stored_output_bytes = 12, }, lifecycle_id, ); return entry_id;}
test "recorded tool finality does not depend on the current turn watermark" { const alloc = std.testing.allocator; for ([_]?u64{ null, 1, 2, 50_000 }) |turn_id| { var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(88, 24, 20) }; defer runtime.deinit(alloc); const id = try appendRecordedWriteForFinalityTest(&runtime, alloc, if (turn_id) |turn| .{ .turn_id = turn, .call_id = "saved-write" } else null); var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(?usize, null), runtime.transcript_release.finality_floor(source.finality, 0, null)); if (turn_id) |turn| { const archived_id = runtime.toolDetailForEntry(id).?.lifecycle_id.?; try std.testing.expectEqual(turn, archived_id.turn_id); try std.testing.expectEqualStrings("saved-write", archived_id.call_id); } }}
test "recorded tool finality stays independent of a live turn with the same number" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(88, 24, 20) }; defer runtime.deinit(alloc); _ = try appendRecordedWriteForFinalityTest(&runtime, alloc, .{ .turn_id = 1, .call_id = "saved-write" }); _ = try runtime.appendRawTranscriptEntry(alloc, "Saved response\n"); const live_id = types.ToolLifecycleId{ .turn_id = 1, .call_id = "live-write" }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = live_id, .reconciles_provisional_call_id = null, .tool_name = "write_file", .activity_kind = .write, } }); for ([_]bool{ false, true }) |terminal| { if (terminal) _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = live_id, .outcome = .{ .kind = .completed, .summary = "Wrote live file" }, } }); var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); const floor = runtime.transcript_release.finality_floor(source.finality, 0, null) orelse return error.TestExpectedLiveFinalityFloor; try std.testing.expect(floor > 0); try std.testing.expect(std.mem.find(u8, source.bytes[0..floor], "Saved response") != null); } _ = try runtime.applyToolLifecycle(alloc, .{ .turn_finished = .{ .turn_id = 1, .outcome = .completed } }); try runtime.finishLifecycleBatch(alloc); var finished = try runtime.prepareTranscriptSource(alloc, null); defer finished.deinit(alloc); try std.testing.expectEqual(@as(?usize, null), runtime.transcript_release.finality_floor(finished.finality, runtime.finalizedToolTurnWatermark(), null));}
test "recorded tool finality survives an owned snapshot after source retirement" { const alloc = std.testing.allocator; var restored = TranscriptRuntime{ .layout = transcriptTestLayout(88, 24, 20) }; defer restored.deinit(alloc); const entry_id = try restored.appendRawTranscriptEntryClassified(alloc, "● Wrote receipt.txt\n", .tool_status); { var original = TranscriptRuntime{ .layout = transcriptTestLayout(88, 24, 20) }; defer original.deinit(alloc); const id = try appendRecordedWriteForFinalityTest(&original, alloc, .{ .turn_id = 2, .call_id = "saved-write" }); var detail = try transcript_store.cloneToolDetailForSnapshot(alloc, original.toolDetailForEntry(id).?.*); errdefer detail.deinit(alloc); detail.entry_id = entry_id; try restored.tool_details.append(alloc, detail); } var source = try restored.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(?usize, null), restored.transcript_release.finality_floor(source.finality, 0, null)); const detail = restored.toolDetailForEntry(entry_id).?; try std.testing.expectEqual(@as(u64, 2), detail.lifecycle_id.?.turn_id); try std.testing.expectEqualStrings("saved-write", detail.lifecycle_id.?.call_id); try std.testing.expectEqualStrings("saved result", detail.result.?);}
test "finality candidates keep tool turn and replaceable tail offsets independent" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(60, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const id = types.ToolLifecycleId{ .turn_id = 5, .call_id = "boundary-call" }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .presentation_group_id = .{ .turn_id = 5, .anchor_step_id = 1 }, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Read boundary file" }, } }); try runtime.finishLifecycleBatch(alloc); _ = try runtime.appendRawTranscriptEntry(alloc, "settled context line\n"); _ = try transcript_store.appendReplaceableTranscriptLineSilent( &runtime, alloc, "working status line", );
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), source.finality.tool_turn_floors.len); const group_floor = source.finality.tool_turn_floors[0]; try std.testing.expectEqual(@as(u64, 5), group_floor.turn_id); try std.testing.expect(group_floor.start_byte < source.bytes.len); // The replaceable-tail contract keeps its own offset; the finality // boundary does not repurpose it. try std.testing.expect(source.replaceable_last_line); try std.testing.expect(source.replaceable_start > group_floor.start_byte); try std.testing.expect(source.replaceable_start < source.bytes.len);}
fn applyCompletedReadForFinalityTest( runtime: *TranscriptRuntime, alloc: Allocator, turn_id: u64, call_id: []const u8, presentation_group_id: ?types.ToolPresentationGroupId,) !void { const id = types.ToolLifecycleId{ .turn_id = turn_id, .call_id = call_id }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .presentation_group_id = presentation_group_id, .reconciles_provisional_call_id = null, .tool_name = "read_file", .activity_kind = .read, } }); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Read finality fixture" }, } });}
test "finality candidates anchor a fully grouped turn at its newest rendered group" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 14, 10), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const group_a = types.ToolPresentationGroupId{ .turn_id = 9, .anchor_step_id = 1 }; for ([_][]const u8{ "group-a-1", "group-a-2", "group-a-3" }) |call_id| { try applyCompletedReadForFinalityTest(&runtime, alloc, 9, call_id, group_a); } _ = try runtime.appendRawTranscriptEntry(alloc, "SECOND_GROUP_INTRO\n"); const group_b = types.ToolPresentationGroupId{ .turn_id = 9, .anchor_step_id = 2 }; try applyCompletedReadForFinalityTest(&runtime, alloc, 9, "group-b-1", group_b);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), source.finality.tool_turn_floors.len); try std.testing.expect(source.finality.mutation_pin_start == null);
const group_a_header = try std.fmt.allocPrint( alloc, "{s}●\x1b[0m {s}3 tool calls · 3 read\x1b[0m", .{ user_message_card.promptMarkerStyle(), ui_render.statusline_style }, ); defer alloc.free(group_a_header); const group_b_header = try std.fmt.allocPrint( alloc, "{s}●\x1b[0m {s}1 tool call · 1 read\x1b[0m", .{ user_message_card.promptMarkerStyle(), ui_render.statusline_style }, ); defer alloc.free(group_b_header); const group_a_start = std.mem.find(u8, source.bytes, group_a_header) orelse return error.TestExpectedGroupAHeader; const group_b_start = std.mem.find(u8, source.bytes, group_b_header) orelse return error.TestExpectedGroupBHeader; try std.testing.expect(group_a_start < group_b_start);
const floor = source.finality.tool_turn_floors[0]; try std.testing.expectEqual(@as(u64, 9), floor.turn_id); try std.testing.expectEqual(group_b_start, floor.start_byte);}
test "finality candidates keep a mixed legacy turn at its earliest rendered tool row" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(80, 14, 10), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const group_a = types.ToolPresentationGroupId{ .turn_id = 10, .anchor_step_id = 1 }; try applyCompletedReadForFinalityTest(&runtime, alloc, 10, "mixed-group-a", group_a); try applyCompletedReadForFinalityTest(&runtime, alloc, 10, "mixed-legacy", null); _ = try runtime.appendRawTranscriptEntry(alloc, "LATEST_GROUP_INTRO\n"); const group_b = types.ToolPresentationGroupId{ .turn_id = 10, .anchor_step_id = 2 }; try applyCompletedReadForFinalityTest(&runtime, alloc, 10, "mixed-group-b", group_b);
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(usize, 1), source.finality.tool_turn_floors.len); try std.testing.expect(source.finality.mutation_pin_start == null);
const group_a_header = try std.fmt.allocPrint( alloc, "{s}●\x1b[0m {s}1 tool call · 1 read\x1b[0m", .{ user_message_card.promptMarkerStyle(), ui_render.statusline_style }, ); defer alloc.free(group_a_header); const earliest_tool_start = std.mem.find(u8, source.bytes, group_a_header) orelse return error.TestExpectedGroupAHeader; const floor = source.finality.tool_turn_floors[0]; try std.testing.expectEqual(@as(u64, 10), floor.turn_id); try std.testing.expectEqual(earliest_tool_start, floor.start_byte);}
test "finality candidates retain the global pin for an unidentified tool row" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(60, 12, 8), .owned_top_row = 1, }; defer runtime.deinit(alloc);
_ = try transcript_store.appendPinnedToolStatusAtomic( &runtime, alloc, "● Running unidentified tool\n", ); var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), source.finality.tool_turn_floors.len); try std.testing.expect(source.finality.mutation_pin_start != null);}
test "grouped completed command rows shell-highlight quoted strings over the row base" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(100, 14, 10), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const id = types.ToolLifecycleId{ .turn_id = 1, .call_id = "cmd-1" }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .presentation_group_id = null, .reconciles_provisional_call_id = null, .tool_name = "shell", .activity_kind = .command, } }); try runtime.setToolCommandMetadata(alloc, id, "printf 'hello world'", "Ran"); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Ran printf 'hello world'" }, } });
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
// The action label keeps the row's ambient style; the command verb and // quoted string pick up syntax palette colors and return to the row base // after their closes. try std.testing.expect(std.mem.find(u8, source.bytes, "Ran \x1b[38;5;245m\x1b[38;5;252mprintf\x1b[39m\x1b[38;5;245m") != null); try std.testing.expect(std.mem.find(u8, source.bytes, "\x1b[38;5;250m'hello world'\x1b[39m\x1b[38;5;245m") != null);}
test "multi-word command labels stay intact when a syntax token follows" { const alloc = std.testing.allocator; var runtime = TranscriptRuntime{ .layout = transcriptTestLayout(100, 14, 10), .owned_top_row = 1, }; defer runtime.deinit(alloc);
const id = types.ToolLifecycleId{ .turn_id = 1, .call_id = "cmd-timeout" }; _ = try runtime.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .presentation_group_id = null, .reconciles_provisional_call_id = null, .tool_name = "shell", .activity_kind = .command, } }); try runtime.setToolCommandMetadata(alloc, id, "sleep 5", "Timed out"); _ = try runtime.applyToolLifecycle(alloc, .{ .terminal = .{ .id = id, .outcome = .{ .kind = .completed, .summary = "Timed out sleep 5" }, } });
var source = try runtime.prepareTranscriptSource(alloc, null); defer source.deinit(alloc);
// A first-space split would open the base style between "Timed" and // "out"; the recorded label keeps both words ahead of the base open. try std.testing.expect(std.mem.find(u8, source.bytes, "Timed out \x1b[38;5;245m") != null); // The command word colors; the bare number argument stays plain. try std.testing.expect(std.mem.find(u8, source.bytes, "\x1b[38;5;252msleep\x1b[39m") != null); try std.testing.expect(std.mem.find(u8, source.bytes, "\x1b[38;5;250m5\x1b[39m") == null);}