@@ -5,7 +5,6 @@ import { hostname } from 'node:os'
55import {
66 AgentStore ,
77 applyEventToMeta ,
8- metaFromEvents ,
98 listAgents ,
109 readLiveMeta ,
1110 readLiveMetas ,
@@ -73,6 +72,16 @@ const RUN: FrameworkEvent[] = [
7372 { kind : 'end' , ok : true } ,
7473]
7574
75+ /** The meta after `events`, folded the way a live run folds them: appended to a real store. */
76+ async function foldLive ( events : readonly FrameworkEvent [ ] , at : string ) : Promise < AgentMeta > {
77+ const store = await AgentStore . open ( CWD , { fs : memFs ( ) , now : at , clock : ( ) => at } )
78+ for ( const event of events ) await store . append ( event )
79+ return store . snapshot ( )
80+ }
81+
82+ /** A run three events in: opened and working, nothing decided yet. What the fold tests refine from. */
83+ const BASE = await foldLive ( RUN . slice ( 0 , 3 ) , AT )
84+
7685test ( 'fresh open truncates the log and writes an initial meta snapshot' , async ( ) => {
7786 const fs = memFs ( { [ EVENTS ] : 'stale\n' } )
7887 const store = await AgentStore . open ( CWD , { fs, fresh : true , now : AT } )
@@ -155,16 +164,8 @@ test('loadEvents on a never-run workspace yields an empty array', async () => {
155164 assert . deepEqual ( await store . loadEvents ( ) , [ ] )
156165} )
157166
158- test ( 'metaFromEvents reconstructs the same snapshot as live appends' , async ( ) => {
159- const meta = metaFromEvents ( RUN , AT )
160- assert . equal ( meta . intent , 'a blog with comments' )
161- assert . equal ( meta . status , 'done' )
162- assert . equal ( meta . startedAt , AT )
163- } )
164-
165167test ( 'applyEventToMeta records the model per leg — the latest session event wins, an unrecorded one clears it (#1438)' , ( ) => {
166- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
167- const first = applyEventToMeta ( base , { kind : 'session' , driver : 'claude' , workspace : '/w' , fake : false , model : 'fable' } , AT )
168+ const first = applyEventToMeta ( BASE , { kind : 'session' , driver : 'claude' , workspace : '/w' , fake : false , model : 'fable' } , AT )
168169 assert . equal ( first . model , 'fable' )
169170 // A continuation leg may run a different model: fold, don't pin.
170171 const second = applyEventToMeta ( first , { kind : 'session' , driver : 'claude' , workspace : '/w' , fake : false , model : 'sonnet' } , AT )
@@ -175,32 +176,29 @@ test('applyEventToMeta records the model per leg — the latest session event wi
175176} )
176177
177178test ( 'applyEventToMeta mirrors the merge arming onto the meta, so a mid-run tab can read it (#1382)' , ( ) => {
178- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
179- const armed = applyEventToMeta ( base , { kind : 'handoff-armed' , push : true , pr : true , merge : true } , AT )
179+ const armed = applyEventToMeta ( BASE , { kind : 'handoff-armed' , push : true , pr : true , merge : true } , AT )
180180 assert . deepEqual ( armed . handoff , { push : true , pr : true , merge : true } )
181181 // A pre-#1382 event has no merge field: the mirror stays shaped like the event, not padded.
182- const old = applyEventToMeta ( base , { kind : 'handoff-armed' , push : true , pr : false } , AT )
182+ const old = applyEventToMeta ( BASE , { kind : 'handoff-armed' , push : true , pr : false } , AT )
183183 assert . deepEqual ( old . handoff , { push : true , pr : false } )
184184} )
185185
186186test ( 'applyEventToMeta folds the handoff report onto the meta, so list surfaces can tell publishing from done (#1455)' , ( ) => {
187- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
188- assert . equal ( base . handoffReport , undefined , 'no report until the epilogue speaks' )
189- const done = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
187+ assert . equal ( BASE . handoffReport , undefined , 'no report until the epilogue speaks' )
188+ const done = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
190189 assert . equal ( done . handoffReport , 'done' )
191- const skipped = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'skipped' , reason : 'not-armed' } , AT )
190+ const skipped = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'skipped' , reason : 'not-armed' } , AT )
192191 assert . equal ( skipped . handoffReport , 'skipped' )
193- const failed = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'failed' , step : 'push' , error : 'boom' } , AT )
192+ const failed = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'failed' , step : 'push' , error : 'boom' } , AT )
194193 assert . equal ( failed . handoffReport , 'failed' )
195194} )
196195
197196test ( 'applyEventToMeta folds the skip reason onto the meta, so the sweep can free a dead claim (#1583)' , ( ) => {
198- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
199- assert . equal ( base . handoffSkip , undefined )
200- const skipped = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'skipped' , reason : 'no-commits' } , AT )
197+ assert . equal ( BASE . handoffSkip , undefined )
198+ const skipped = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'skipped' , reason : 'no-commits' } , AT )
201199 assert . equal ( skipped . handoffSkip , 'no-commits' )
202200 // A handoff that ran carries no skip: the field means "why nothing happened", nothing else.
203- const done = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
201+ const done = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
204202 assert . equal ( done . handoffSkip , undefined )
205203 // And a later leg that published clears a stale skip — a resumed run's second handoff can
206204 // publish after its first skipped, and the release must not act on leg one's reason.
@@ -209,34 +207,30 @@ test('applyEventToMeta folds the skip reason onto the meta, so the sweep can fre
209207} )
210208
211209test ( 'applyEventToMeta folds the merge outcome onto the meta, so the CI watch can find its PRs (#1418)' , ( ) => {
212- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
213- assert . equal ( base . mergeOutcome , undefined )
214- const watched = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'done' , pushed : true , merge : { outcome : 'watched' } } , AT )
210+ assert . equal ( BASE . mergeOutcome , undefined )
211+ const watched = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'done' , pushed : true , merge : { outcome : 'watched' } } , AT )
215212 assert . equal ( watched . mergeOutcome , 'watched' )
216213 // The already-open skip carries a merge too (#1216): a rerun finding its predecessor's PR.
217- const armed = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'skipped' , reason : 'already-open' , merge : { outcome : 'auto-armed' } } , AT )
214+ const armed = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'skipped' , reason : 'already-open' , merge : { outcome : 'auto-armed' } } , AT )
218215 assert . equal ( armed . mergeOutcome , 'auto-armed' )
219- const noMerge = applyEventToMeta ( base , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
216+ const noMerge = applyEventToMeta ( BASE , { kind : 'handoff' , outcome : 'done' , pushed : true } , AT )
220217 assert . equal ( noMerge . mergeOutcome , undefined , 'a handoff without a merge half folds nothing' )
221218} )
222219
223220test ( 'applyEventToMeta marks a thrown run as failed' , ( ) => {
224- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
225- const failed = applyEventToMeta ( base , { kind : 'end' , ok : false , detail : 'boom' } , AT )
221+ const failed = applyEventToMeta ( BASE , { kind : 'end' , ok : false , detail : 'boom' } , AT )
226222 assert . equal ( failed . status , 'failed' )
227223} )
228224
229225test ( 'applyEventToMeta marks a user-stopped run as stopped, not failed (#218)' , ( ) => {
230- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
231- const stopped = applyEventToMeta ( base , { kind : 'end' , ok : false , stopped : true } , AT )
226+ const stopped = applyEventToMeta ( BASE , { kind : 'end' , ok : false , stopped : true } , AT )
232227 assert . equal ( stopped . status , 'stopped' )
233228} )
234229
235230test ( 'applyEventToMeta tracks whether the run is working or parked on the user (#785)' , ( ) => {
236- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
237- assert . equal ( base . settledAt , undefined , 'a working run is not parked' )
231+ assert . equal ( BASE . settledAt , undefined , 'a working run is not parked' )
238232
239- const parked = applyEventToMeta ( base , { kind : 'settled' } , AT )
233+ const parked = applyEventToMeta ( BASE , { kind : 'settled' } , AT )
240234 assert . equal ( parked . settledAt , AT )
241235 assert . equal ( parked . status , 'running' , 'still live: it holds the project and takes messages' )
242236
@@ -245,26 +239,24 @@ test('applyEventToMeta tracks whether the run is working or parked on the user (
245239 assert . equal ( working . settledAt , undefined )
246240
247241 // An agent that has ended is not waiting on anyone.
248- const ended = applyEventToMeta ( applyEventToMeta ( base , { kind : 'settled' } , AT ) , { kind : 'end' , ok : true } , AT )
242+ const ended = applyEventToMeta ( applyEventToMeta ( BASE , { kind : 'settled' } , AT ) , { kind : 'end' , ok : true } , AT )
249243 assert . equal ( ended . settledAt , undefined )
250244 assert . equal ( ended . status , 'done' )
251245} )
252246
253247test ( 'applyEventToMeta records the session name + ready-for-merge lifecycle signals (#326)' , ( ) => {
254- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
255- assert . equal ( base . sessionName , undefined )
256- assert . equal ( base . readyForMerge , undefined )
257- const named = applyEventToMeta ( base , { kind : 'session-name' , name : 'add-comments' } , AT )
248+ assert . equal ( BASE . sessionName , undefined )
249+ assert . equal ( BASE . readyForMerge , undefined )
250+ const named = applyEventToMeta ( BASE , { kind : 'session-name' , name : 'add-comments' } , AT )
258251 assert . equal ( named . sessionName , 'add-comments' )
259252 const ready = applyEventToMeta ( named , { kind : 'ready-for-merge' } , AT )
260253 assert . equal ( ready . readyForMerge , true )
261254 assert . equal ( ready . sessionName , 'add-comments' ) // ready doesn't clobber the name
262255} )
263256
264257test ( 'applyEventToMeta records the ticket a run is implementing (#1117)' , ( ) => {
265- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
266- assert . equal ( base . ticket , undefined , 'a run nobody linked to a ticket says nothing' )
267- const on = applyEventToMeta ( base , { kind : 'ticket' , path : 'tickets/2026-07-25_login.md' } , AT )
258+ assert . equal ( BASE . ticket , undefined , 'a run nobody linked to a ticket says nothing' )
259+ const on = applyEventToMeta ( BASE , { kind : 'ticket' , path : 'tickets/2026-07-25_login.md' } , AT )
268260 assert . equal ( on . ticket , 'tickets/2026-07-25_login.md' )
269261 // It is a fact about why the agent exists, so it outlives the work: a reader looking at a finished
270262 // run still gets to see which ticket it was.
@@ -273,9 +265,8 @@ test('applyEventToMeta records the ticket a run is implementing (#1117)', () =>
273265} )
274266
275267test ( 'applyEventToMeta tracks the pending choice gate a run is parked on (#636)' , ( ) => {
276- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
277- assert . equal ( base . pendingChoice , undefined )
278- const asked = applyEventToMeta ( base , { kind : 'choice' , id : 'g1' , title : 'Cache the auth store?' , options : [ { id : 'y' , label : 'Yes' } ] } , AT )
268+ assert . equal ( BASE . pendingChoice , undefined )
269+ const asked = applyEventToMeta ( BASE , { kind : 'choice' , id : 'g1' , title : 'Cache the auth store?' , options : [ { id : 'y' , label : 'Yes' } ] } , AT )
279270 assert . deepEqual ( asked . pendingChoice , { id : 'g1' , title : 'Cache the auth store?' } )
280271 // A resolve for a different gate id leaves it parked; the matching resolve clears it.
281272 const other = applyEventToMeta ( asked , { kind : 'choice-resolved' , id : 'other' , picked : 'y' , by : 'user' } , AT )
@@ -285,8 +276,7 @@ test('applyEventToMeta tracks the pending choice gate a run is parked on (#636)'
285276} )
286277
287278test ( 'applyEventToMeta clears a pending choice when the run ends (#636)' , ( ) => {
288- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
289- const asked = applyEventToMeta ( base , { kind : 'choice' , id : 'g1' , title : 'q?' , options : [ { id : 'y' , label : 'Yes' } ] } , AT )
279+ const asked = applyEventToMeta ( BASE , { kind : 'choice' , id : 'g1' , title : 'q?' , options : [ { id : 'y' , label : 'Yes' } ] } , AT )
290280 const ended = applyEventToMeta ( asked , { kind : 'end' , ok : true } , AT )
291281 assert . equal ( ended . pendingChoice , undefined )
292282} )
@@ -891,17 +881,15 @@ test('startedAtFromAgentId inverts agentIdFromStartedAt, and refuses foreign ids
891881test ( 'applyEventToMeta records the pull request a session opened (E6)' , ( ) => {
892882 // The number is a fact about the agent, so the agent writes it down — rather than every later
893883 // surface re-deriving it from branch names and creation times.
894- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
895- assert . equal ( base . pr , undefined , 'a session with no PR says nothing' )
896- const on = applyEventToMeta ( base , { kind : 'pull-request' , number : 42 , url : 'https://x/pull/42' } , AT )
884+ assert . equal ( BASE . pr , undefined , 'a session with no PR says nothing' )
885+ const on = applyEventToMeta ( BASE , { kind : 'pull-request' , number : 42 , url : 'https://x/pull/42' } , AT )
897886 assert . deepEqual ( on . pr , { number : 42 , url : 'https://x/pull/42' } )
898887 // It must outlive the agent: every read of it happens after the session has ended.
899888 assert . deepEqual ( applyEventToMeta ( on , { kind : 'end' , ok : true } , AT ) . pr , { number : 42 , url : 'https://x/pull/42' } )
900889} )
901890
902891test ( 'applyEventToMeta records the branch a branch event names (#1277)' , ( ) => {
903- const base = metaFromEvents ( RUN . slice ( 0 , 3 ) , AT )
904- const on = applyEventToMeta ( base , { kind : 'branch' , branch : 'tf-agent-r1' } , AT )
892+ const on = applyEventToMeta ( BASE , { kind : 'branch' , branch : 'tf-agent-r1' } , AT )
905893 assert . equal ( on . branch , 'tf-agent-r1' )
906894 // A rename mid-run replaces it: the meta always names the branch the work is on now.
907895 const renamed = applyEventToMeta ( on , { kind : 'branch' , branch : 'tf-cool-name' } , AT )
0 commit comments