ReviewOS

also looking at this

stacks/wildloop

feat: native trail maps and complete native mobile screens

#1017
Draft chris wants to merge codex/trail-experience into main
60 files +2505 -823

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

tests/unit/native-record-screen.test.tsmodified+43-0
Changes to tests/unit/native-record-screen.test.ts
@@ -40,6 +40,49 @@ describe('native Record checkpoint recovery',()=>{
4040 expect(requests(screen).some(r=>r.payload.module==='Network')).toBe(false)
4141 expect(shownText(screen).some(text=>text.includes('moving ·'))).toBe(true)
4242 })
43 it('retains an existing checkpoint when native registration loses a race',async()=>{
44 const screen=await start('Record',{},['fetch','secureStorage','storage','storageCompareAndSet','storageSetIfAbsent','crypto','geolocation','locationRecordingOwnership','locationRecordingIntentIdentity','lifecycle'],new Map([['auth_token','record-token'],['wildloop-account',JSON.stringify({token:'record-token',user:{id:7}})]]))
45 const stored=await waitForRequest(screen,0,r=>r.payload.module==='Storage' && r.payload.method==='get')
46 answer(screen,stored,null)
47 const initial=await waitForRequest(screen,0,r=>r.payload.method==='getRecordingState')
48 answer(screen,initial,{active:false,paused:false})
49 await Bun.sleep(0)
50 await screen.event('record-start','onPress')
51 const host=await waitForRequest(screen,requests(screen).indexOf(initial)+1,r=>r.payload.method==='getRecordingState')
52 answer(screen,host,{active:false,paused:false})
53 const uuid=await waitForRequest(screen,0,r=>r.payload.module==='Crypto')
54 answer(screen,uuid,'803798f7-733a-4692-9ca5-6dca46acc6e2')
55 const create=await waitForRequest(screen,0,r=>r.payload.method==='setIfAbsent')
56 answer(screen,create,false)
57 const replace=await waitForRequest(screen,0,r=>r.payload.method==='compareAndSet')
58 expect(replace.payload.args[1]).toBeNull()
59 answer(screen,replace,false)
60 await waitForText(screen,'The saved recording changed on this device. Pull down to recover it before trying again. Your track has been retained.')
61 expect(requests(screen).some(r=>r.payload.method==='startRecording' || r.payload.method==='set' || r.payload.method==='remove')).toBe(false)
62 })
63 it('acknowledges only the completed owner and never clears a replaced checkpoint',async()=>{
64 const saved={...checkpoint,phase:'stopped',endedAt:160000}
65 const screen=await start('Record',{},['fetch','secureStorage','storage','storageCompareAndSet','geolocation','locationRecordingOwnership','lifecycle'],new Map([['auth_token','record-token'],['wildloop-account',JSON.stringify({token:'record-token',user:{id:7}})]]))
66 const stored=await waitForRequest(screen,0,r=>r.payload.module==='Storage' && r.payload.method==='get')
67 answer(screen,stored,saved)
68 await waitForText(screen,'Your finished recording is saved on this device. Retry the upload when connected.')
69 await screen.event('record-retry','onPress')
70 const me=await waitForRequest(screen,0,r=>r.payload.module==='Network')
71 answer(screen,me,ok({user:{id:7}}))
72 const post=await waitForRequest(screen,requests(screen).indexOf(me)+1,r=>r.payload.module==='Network')
73 expect(requests(screen).some(r=>r.payload.method==='acknowledgeRecording')).toBe(false)
74 answer(screen,post,ok({activity:{id:99,captureEligible:false}},201))
75 const ack=await waitForRequest(screen,0,r=>r.payload.method==='acknowledgeRecording')
76 expect(ack.payload.args[0]).toEqual({expectedId:'run-7'})
77 expect(requests(screen).some(r=>r.payload.method==='compareAndSet')).toBe(false)
78 answer(screen,ack,true)
79 const clear=await waitForRequest(screen,0,r=>r.payload.method==='compareAndSet')
80 expect(clear.payload.args).toEqual(['wildloop-native-recording-v1',saved,null])
81 answer(screen,clear,false)
82 await waitForText(screen,'The saved recording changed on this device. Pull down to recover it before trying again. Your track has been retained.')
83 expect(requests(screen).some(r=>r.payload.method==='remove')).toBe(false)
84 expect(screen.sent.some(r=>r.type==='NAVIGATE')).toBe(false)
85 })
4386 it('does not reveal or upload another account’s saved track',async()=>{
4487 const screen=await recover(8)
4588 await waitForText(screen,'Log in as the account that started the saved recording before resuming or uploading it.')