2020from utils .text_extract import extract_text_from_bubble
2121from utils .tool_parser import parse_tool_call as _parse_tool_call
2222from utils .workspace_descriptor import read_json_file
23- from models import Bubble , Composer , SchemaError
23+ from models import Bubble , Composer , ParseWarningCollector , SchemaError
2424from services .workspace_db import (
2525 _build_composer_id_to_workspace_id ,
2626 _collect_invalid_workspace_ids ,
@@ -83,6 +83,7 @@ def assemble_workspace_tabs(
8383 rules : list ,
8484) -> tuple [dict , int ]:
8585 """Build (payload, status) for GET /api/workspaces/<id>/tabs; status=404 if global storage is missing."""
86+ parse_warnings = ParseWarningCollector ()
8687 response : dict = {"tabs" : []}
8788
8889 workspace_entries = _collect_workspace_entries (workspace_path )
@@ -142,6 +143,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
142143 "Skipping Bubble cursorDiskKV row with NULL value: key=%r" ,
143144 row ["key" ],
144145 )
146+ parse_warnings .record_bubble_skipped ()
145147 continue
146148 try :
147149 parsed = json .loads (row ["value" ])
@@ -155,6 +157,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
155157 payload_len ,
156158 payload_fp ,
157159 )
160+ parse_warnings .record_bubble_skipped ()
158161 continue
159162 try :
160163 bubble_obj = Bubble .from_dict (parsed , bubble_id = bid )
@@ -168,6 +171,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
168171 bid ,
169172 e ,
170173 )
174+ parse_warnings .record_bubble_skipped ()
171175
172176 # Load codeBlockDiffs
173177 code_block_diff_map = load_code_block_diff_map (global_db )
@@ -232,6 +236,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
232236 "Skipping Composer cursorDiskKV row with NULL value: key=%r" ,
233237 row ["key" ],
234238 )
239+ parse_warnings .record_composer_skipped ()
235240 continue
236241 try :
237242 parsed = json .loads (row ["value" ])
@@ -245,6 +250,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
245250 payload_len ,
246251 payload_fp ,
247252 )
253+ parse_warnings .record_composer_skipped ()
248254 continue
249255 try :
250256 composer = Composer .from_dict (parsed , composer_id = composer_id )
@@ -257,6 +263,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
257263 composer_id ,
258264 e ,
259265 )
266+ parse_warnings .record_composer_skipped ()
260267 continue
261268 try :
262269 cd = composer .raw
@@ -579,8 +586,9 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
579586 composer_id ,
580587 e ,
581588 )
589+ parse_warnings .record_composer_skipped ()
582590
583591 # Sort tabs by timestamp descending (newest first)
584592 response ["tabs" ].sort (key = lambda t : t .get ("timestamp" ) or 0 , reverse = True )
585593
586- return response , 200
594+ return parse_warnings . attach_to ( response ) , 200
0 commit comments