Skip to content

Commit 0af0e34

Browse files
committed
fix: eval.md findings
1 parent b5d5a4c commit 0af0e34

7 files changed

Lines changed: 58 additions & 26 deletions

File tree

api/export_api.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
from datetime import datetime
1414
from pathlib import Path
1515

16-
from flask import Blueprint, Response, current_app, jsonify, request
16+
from flask import Blueprint, Response, jsonify, request
17+
18+
from api.flask_config import exclusion_rules
1719

1820
from utils.workspace_path import resolve_workspace_path
1921
from utils.path_helpers import to_epoch_ms
@@ -115,7 +117,7 @@ def export_chats():
115117

116118
today = datetime.now().strftime("%Y-%m-%d")
117119
exported = []
118-
rules = current_app.config.get("EXCLUSION_RULES") or []
120+
rules = exclusion_rules()
119121

120122
# ── Database reading via service layer ────────────────────────────────
121123
with open_global_db(workspace_path) as (global_db, _):
@@ -142,7 +144,11 @@ def export_chats():
142144
if not headers:
143145
continue
144146

145-
updated_at_ms = to_epoch_ms(cd.get("lastUpdatedAt")) or to_epoch_ms(cd.get("createdAt")) or 0
147+
updated_at_ms = to_epoch_ms(cd.get("lastUpdatedAt"))
148+
if updated_at_ms is None:
149+
updated_at_ms = to_epoch_ms(cd.get("createdAt"))
150+
if updated_at_ms is None:
151+
updated_at_ms = 0
146152
if since == "last" and updated_at_ms and updated_at_ms <= last_export_ms:
147153
continue
148154

api/flask_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Shared Flask request/config helpers for API blueprints."""
2+
3+
from __future__ import annotations
4+
5+
from flask import current_app
6+
7+
8+
def exclusion_rules() -> list:
9+
"""Return loaded exclusion rules from app config (empty list when unset)."""
10+
return current_app.config.get("EXCLUSION_RULES") or []

api/workspaces.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import os
1212
from datetime import datetime, timezone
1313

14-
from flask import Blueprint, current_app, jsonify
14+
from flask import Blueprint, jsonify
15+
16+
from api.flask_config import exclusion_rules
1517

1618
from utils.workspace_path import resolve_workspace_path, get_cli_chats_path
1719
from utils.cli_chat_reader import list_cli_projects
@@ -40,11 +42,6 @@
4042
_logger = logging.getLogger(__name__)
4143

4244

43-
def _exclusion_rules() -> list:
44-
"""Return loaded exclusion rules from app config (empty list when unset)."""
45-
return current_app.config.get("EXCLUSION_RULES") or []
46-
47-
4845
# ---------------------------------------------------------------------------
4946
# GET /api/workspaces
5047
# ---------------------------------------------------------------------------
@@ -53,7 +50,7 @@ def _exclusion_rules() -> list:
5350
def list_workspaces():
5451
try:
5552
workspace_path = resolve_workspace_path()
56-
rules = _exclusion_rules()
53+
rules = exclusion_rules()
5754
projects, warnings = list_workspace_projects(workspace_path, rules)
5855
payload: dict = {"projects": projects}
5956
if warnings:
@@ -150,13 +147,13 @@ def get_workspace(workspace_id):
150147
def get_workspace_tabs(workspace_id):
151148
if workspace_id.startswith("cli:"):
152149
try:
153-
return get_cli_workspace_tabs(workspace_id)
150+
return get_cli_workspace_tabs(workspace_id, exclusion_rules())
154151
except Exception:
155152
_logger.exception("Failed to get CLI workspace tabs")
156153
return jsonify({"error": "Failed to get workspace tabs"}), 500
157154
try:
158155
workspace_path = resolve_workspace_path()
159-
rules = _exclusion_rules()
156+
rules = exclusion_rules()
160157
payload, status = assemble_workspace_tabs(workspace_id, workspace_path, rules)
161158
return jsonify(payload), status
162159
except Exception:

scripts/export.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,23 @@ def main():
268268
composer_id = row["key"].split(":")[1]
269269
try:
270270
cd = json.loads(row["value"])
271-
except Exception:
271+
except (json.JSONDecodeError, ValueError) as parse_err:
272+
_logger.debug(
273+
"Skipping corrupt composerData row %s: %s",
274+
composer_id,
275+
parse_err,
276+
)
272277
continue
273278

274279
headers = cd.get("fullConversationHeadersOnly") or []
275280
if not headers:
276281
continue
277282

278-
updated_at = to_epoch_ms(cd.get("lastUpdatedAt")) or to_epoch_ms(cd.get("createdAt")) or 0
283+
updated_at = to_epoch_ms(cd.get("lastUpdatedAt"))
284+
if updated_at is None:
285+
updated_at = to_epoch_ms(cd.get("createdAt"))
286+
if updated_at is None:
287+
updated_at = 0
279288
if since == "last" and updated_at <= last_export:
280289
continue
281290

services/cli_tabs.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
import logging
44
from datetime import datetime
55

6-
from flask import current_app, jsonify
7-
8-
_logger = logging.getLogger(__name__)
6+
from flask import jsonify
97

108
from utils.cli_chat_reader import list_cli_projects, messages_to_bubbles, traverse_blobs
119
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
1210
from utils.workspace_path import get_cli_chats_path
1311

12+
_logger = logging.getLogger(__name__)
13+
1414

15-
def get_cli_workspace_tabs(workspace_id: str):
15+
def get_cli_workspace_tabs(workspace_id: str, rules: list):
1616
"""Return Flask JSON response with tabs for a Cursor CLI project.
1717
1818
Args:
1919
workspace_id: Workspace id with ``cli:`` prefix (e.g. ``cli:proj-1``).
20+
rules: Exclusion rule token lists (same as :func:`services.workspace_tabs.assemble_workspace_tabs`).
2021
2122
Returns:
2223
``flask.Response | tuple[flask.Response, int]`` suitable for a Flask route
@@ -37,7 +38,6 @@ def get_cli_workspace_tabs(workspace_id: str):
3738
if project is None:
3839
return jsonify({"error": "CLI project not found"}), 404
3940

40-
rules = current_app.config.get("EXCLUSION_RULES") or []
4141
ws_name = project.get("workspace_name") or project_id[:12]
4242
sessions = project.get("sessions") or []
4343
if not isinstance(sessions, list):

services/workspace_resolver.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,12 @@ def infer_workspace_name_from_context(workspace_path: str, workspace_id: str) ->
111111
continue
112112
try:
113113
ctx = json.loads(row[0])
114-
except Exception:
114+
except (json.JSONDecodeError, ValueError) as e:
115+
_logger.debug(
116+
"Skipping malformed messageRequestContext for %s: %s",
117+
cid,
118+
e,
119+
)
115120
continue
116121
layouts = ctx.get("projectLayouts")
117122
if not isinstance(layouts, list):
@@ -121,7 +126,12 @@ def infer_workspace_name_from_context(workspace_path: str, workspace_id: str) ->
121126
if isinstance(layout, str):
122127
try:
123128
obj = json.loads(layout)
124-
except Exception:
129+
except (json.JSONDecodeError, ValueError) as e:
130+
_logger.debug(
131+
"Skipping malformed projectLayout for %s: %s",
132+
cid,
133+
e,
134+
)
125135
obj = None
126136
elif isinstance(layout, dict):
127137
obj = layout

tests/test_cli_tabs.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def fake_traverse_blobs(db_path):
5959
patch("services.cli_tabs.list_cli_projects", return_value=[project]), \
6060
patch("services.cli_tabs.traverse_blobs", side_effect=fake_traverse_blobs), \
6161
patch("services.cli_tabs.messages_to_bubbles", side_effect=fake_messages_to_bubbles):
62-
response = get_cli_workspace_tabs("cli:proj-1")
62+
response = get_cli_workspace_tabs("cli:proj-1", [])
6363

6464
self.assertEqual(response.status_code, 200)
6565
payload = response.get_json()
@@ -86,7 +86,7 @@ def fake_messages_to_bubbles(messages, created_ms):
8686
patch("services.cli_tabs.list_cli_projects", return_value=[project]), \
8787
patch("services.cli_tabs.traverse_blobs", side_effect=fake_traverse_blobs), \
8888
patch("services.cli_tabs.messages_to_bubbles", side_effect=fake_messages_to_bubbles):
89-
response = get_cli_workspace_tabs("cli:proj-1")
89+
response = get_cli_workspace_tabs("cli:proj-1", [])
9090

9191
self.assertEqual(response.status_code, 200)
9292
payload = response.get_json()
@@ -113,7 +113,7 @@ def fake_messages_to_bubbles(messages, created_ms):
113113
patch("services.cli_tabs.list_cli_projects", return_value=garbage_then_real), \
114114
patch("services.cli_tabs.traverse_blobs", side_effect=fake_traverse_blobs), \
115115
patch("services.cli_tabs.messages_to_bubbles", side_effect=fake_messages_to_bubbles):
116-
response = get_cli_workspace_tabs("cli:proj-1")
116+
response = get_cli_workspace_tabs("cli:proj-1", [])
117117

118118
self.assertEqual(response.status_code, 200)
119119
payload = response.get_json()
@@ -131,7 +131,7 @@ def test_project_missing_workspace_name_uses_fallback(self) -> None:
131131
patch("services.cli_tabs.traverse_blobs", return_value=["ok"]), \
132132
patch("services.cli_tabs.messages_to_bubbles",
133133
return_value=[{"type": "user", "text": "hi", "timestamp": 1}]):
134-
response = get_cli_workspace_tabs("cli:proj-min")
134+
response = get_cli_workspace_tabs("cli:proj-min", [])
135135

136136
self.assertEqual(response.status_code, 200)
137137
# Tab still rendered — ws_name fallback (project_id[:12]) used for searchable text.
@@ -144,7 +144,7 @@ def test_project_missing_sessions_returns_200_empty_tabs(self) -> None:
144144

145145
with app.test_request_context("/api/workspaces/cli:proj-empty/tabs"), \
146146
patch("services.cli_tabs.list_cli_projects", return_value=[project]):
147-
response = get_cli_workspace_tabs("cli:proj-empty")
147+
response = get_cli_workspace_tabs("cli:proj-empty", [])
148148

149149
self.assertEqual(response.status_code, 200)
150150
self.assertEqual(response.get_json(), {"tabs": []})

0 commit comments

Comments
 (0)