Version
Wave 0.14.5, Windows 11, pwsh 7 shells.
Symptom
A blank column stays in the tab where a block used to be. It persists across Wave restarts. Inspecting db_layout shows a leaf whose blockId is no longer present in tab.blockids / db_block -- the block row is gone but the layout leaf is not.
Repro
- Create a widget/block with
cmd:closeonexit: true (or from inside a block run wsh deleteblock -b <id>).
- Let the shell command exit with code 0.
- After
cmd:closeonexitdelay, the block row is deleted -- but the layout leaf for it remains as an empty column/leaf in the tab.
Log signature observed: a DeleteBlock: parentBlockCount: N line appears roughly 1.5s after ShellProcStatus: done, with no corresponding layout update.
Root cause (source refs, verified at tag v0.14.5)
pkg/blockcontroller/shellcontroller.go line 650, checkCloseOnExit, calls wshclient.DeleteBlockCommand at line 669.
pkg/wshrpc/wshserver/wshserver.go line 474, (ws *WshServer) DeleteBlockCommand, calls wcore.DeleteBlock(ctx, data.BlockId, true) at line 486.
pkg/wcore/block.go line 153, DeleteBlock, removes the block and (via deleteBlockObj) emits wps.Event_BlockClose at line 237.
frontend/app/store/global.ts line 55, initGlobalWaveEventSubs, subscribes to waveobj:update, config, waveai:modeconfig, userinput, blockfile, and waveai:ratelimit -- there is no subscription to a blockclose event anywhere in this function, so nothing dispatches LayoutTreeActionType.DeleteNode when the backend deletes a block on its own initiative.
frontend/layout/lib/layoutModel.ts line 411, cleanupOrphanedBlocks, only walks tab.blockids and checks whether each blockId exists in the layout tree (removing the ones that don't) -- it never walks the layout tree to find leaves whose blockId is absent from tab.blockids. So a leaf orphaned by a backend-initiated delete is never reconciled, even across a restart when cleanupOrphanedBlocks runs again.
Suggested fix
Either:
- Subscribe to the block-close event on the frontend (
frontend/app/store/global.ts) and dispatch LayoutTreeActionType.DeleteNode against the affected tab's layout model, or
- Extend
cleanupOrphanedBlocks (frontend/layout/lib/layoutModel.ts) to also prune layout leaves whose blockId is not present in tab.blockids, not just the reverse case it currently handles.
Workaround we use
An external script that deletes dangling leaves from db_layout while Wave is closed.
Report generated with help from Claude Code.
Version
Wave 0.14.5, Windows 11, pwsh 7 shells.
Symptom
A blank column stays in the tab where a block used to be. It persists across Wave restarts. Inspecting
db_layoutshows a leaf whoseblockIdis no longer present intab.blockids/db_block-- the block row is gone but the layout leaf is not.Repro
cmd:closeonexit: true(or from inside a block runwsh deleteblock -b <id>).cmd:closeonexitdelay, the block row is deleted -- but the layout leaf for it remains as an empty column/leaf in the tab.Log signature observed: a
DeleteBlock: parentBlockCount: Nline appears roughly 1.5s afterShellProcStatus: done, with no corresponding layout update.Root cause (source refs, verified at tag v0.14.5)
pkg/blockcontroller/shellcontroller.goline 650,checkCloseOnExit, callswshclient.DeleteBlockCommandat line 669.pkg/wshrpc/wshserver/wshserver.goline 474,(ws *WshServer) DeleteBlockCommand, callswcore.DeleteBlock(ctx, data.BlockId, true)at line 486.pkg/wcore/block.goline 153,DeleteBlock, removes the block and (viadeleteBlockObj) emitswps.Event_BlockCloseat line 237.frontend/app/store/global.tsline 55,initGlobalWaveEventSubs, subscribes towaveobj:update,config,waveai:modeconfig,userinput,blockfile, andwaveai:ratelimit-- there is no subscription to ablockcloseevent anywhere in this function, so nothing dispatchesLayoutTreeActionType.DeleteNodewhen the backend deletes a block on its own initiative.frontend/layout/lib/layoutModel.tsline 411,cleanupOrphanedBlocks, only walkstab.blockidsand checks whether each blockId exists in the layout tree (removing the ones that don't) -- it never walks the layout tree to find leaves whoseblockIdis absent fromtab.blockids. So a leaf orphaned by a backend-initiated delete is never reconciled, even across a restart whencleanupOrphanedBlocksruns again.Suggested fix
Either:
frontend/app/store/global.ts) and dispatchLayoutTreeActionType.DeleteNodeagainst the affected tab's layout model, orcleanupOrphanedBlocks(frontend/layout/lib/layoutModel.ts) to also prune layout leaves whoseblockIdis not present intab.blockids, not just the reverse case it currently handles.Workaround we use
An external script that deletes dangling leaves from
db_layoutwhile Wave is closed.Report generated with help from Claude Code.