@@ -27,6 +27,7 @@ const makeStubStorage = () => {
2727 updatedAt : new Date ( 0 ) ,
2828 } ;
2929 } ;
30+ let failNextWrites = 0 ;
3031 const unsupported = ( member : string ) => ( ) =>
3132 Effect . die ( `stub storage does not implement ${ member } ` ) ;
3233 const storage : PluginStorageFacade = {
@@ -43,16 +44,27 @@ const makeStubStorage = () => {
4344 getForOwner : ( input ) => Effect . sync ( ( ) => entryFor ( input . key ) ) ,
4445 list : unsupported ( "list" ) ,
4546 put : ( input ) =>
46- Effect . sync ( ( ) => {
47+ Effect . suspend ( ( ) => {
48+ if ( failNextWrites > 0 ) {
49+ failNextWrites -= 1 ;
50+ return Effect . fail ( { _tag : "StorageError" as const } ) as never ;
51+ }
4752 writes += 1 ;
4853 rows . set ( input . key , input . data ) ;
49- return entryFor ( input . key ) as never ;
54+ return Effect . sync ( ( ) => entryFor ( input . key ) as never ) ;
5055 } ) ,
5156 putMany : unsupported ( "putMany" ) ,
5257 remove : unsupported ( "remove" ) ,
5358 removeMany : unsupported ( "removeMany" ) ,
5459 } ;
55- return { storage, rows, writeCount : ( ) => writes } ;
60+ return {
61+ storage,
62+ rows,
63+ writeCount : ( ) => writes ,
64+ failWrites : ( count : number ) => {
65+ failNextWrites = count ;
66+ } ,
67+ } ;
5668} ;
5769
5870const HOUR = 60 * 60 * 1000 ;
@@ -120,6 +132,24 @@ describe("makeShapeMemory", () => {
120132 } ) ,
121133 ) ;
122134
135+ it . effect ( "retries after a failed write instead of pretending it persisted" , ( ) =>
136+ Effect . gen ( function * ( ) {
137+ const stub = makeStubStorage ( ) ;
138+ const memory = makeShapeMemory ( stub . storage ) ;
139+
140+ stub . failWrites ( 1 ) ;
141+ yield * memory . observe ( ADDRESS , OWNER , "direct" , { id : 1 } ) ;
142+ expect ( stub . rows . has ( ADDRESS ) , "the failed write stored nothing" ) . toBe ( false ) ;
143+
144+ // The very next observation retries — no waiting out the freshness
145+ // interval on bookkeeping that lied about persisting.
146+ yield * memory . observe ( ADDRESS , OWNER , "direct" , { id : 2 } ) ;
147+ expect ( stub . rows . has ( ADDRESS ) , "the retry persisted" ) . toBe ( true ) ;
148+ const stored = stub . rows . get ( ADDRESS ) as { observations : number } ;
149+ expect ( stored . observations ) . toBe ( 2 ) ;
150+ } ) ,
151+ ) ;
152+
123153 it . effect ( "treats legacy records without a contract field as direct" , ( ) =>
124154 Effect . gen ( function * ( ) {
125155 const stub = makeStubStorage ( ) ;
0 commit comments