@@ -188,6 +188,36 @@ describe('applyWorkflowOperations', () => {
188188 expect ( result . needsRedeployment ) . toBe ( true )
189189 } )
190190
191+ /**
192+ * Replacing a draft with `{ blocks: {}, edges: [] }` legitimately deletes
193+ * every normalized block row. The workflow row still exists, so the next add
194+ * must initialize that empty canvas rather than treating it as missing state.
195+ */
196+ it ( 'adds the first block to a blockless workflow' , async ( ) => {
197+ const emptyGraph = graph ( { } )
198+ const graphWithAddedBlock = graph ( {
199+ 'block-2' : { ...BLOCK , id : 'block-2' , type : 'agent' , name : 'Triage' } ,
200+ } )
201+ mocks . loadNormalized . mockResolvedValue ( emptyGraph )
202+ mocks . normalizeState . mockImplementation ( ( state ) => ( { state, warnings : [ ] } ) )
203+ mocks . applyOperations . mockReturnValue ( {
204+ state : graphWithAddedBlock ,
205+ validationErrors : [ ] ,
206+ skippedItems : [ ] ,
207+ mintedBlockIds : { } ,
208+ } )
209+
210+ const result = await applyWorkflowOperations . execute ( {
211+ principal : sessionPrincipal ,
212+ input : { workflowId : 'workflow-1' , operations, atomic : true } ,
213+ } )
214+
215+ expect ( mocks . normalizeState ) . toHaveBeenCalledWith ( emptyGraph )
216+ expect ( mocks . applyOperations ) . toHaveBeenCalledWith ( emptyGraph , operations , null )
217+ expect ( mocks . replace ) . toHaveBeenCalledTimes ( 1 )
218+ expect ( result . graph . blocks ) . toEqual ( graphWithAddedBlock . blocks )
219+ } )
220+
191221 describe ( 'dry run' , ( ) => {
192222 it ( 'runs the whole engine and stops at the write' , async ( ) => {
193223 const result = await applyWorkflowOperations . execute ( {
0 commit comments