Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,10 @@ jobs:

# ── Typecheck: mypy ───────────────────────────────────────────────────────
# Codebase already has type hints across most of the surface (~70+ typed
# functions). Mypy runs in lenient mode (--ignore-missing-imports for
# untyped third-party deps; no strict-optional) so the gate isn't a wall
# of false positives. The transitional `continue-on-error: true` was
# removed in #29 once mypy reached zero errors on this repo — type
# failures now block merges.
# functions). Mypy runs with --ignore-missing-imports for untyped
# third-party deps; strict-optional is enabled (mypy default). The
# transitional `continue-on-error: true` was removed in #29 once mypy
# reached zero errors on this repo — type failures now block merges.
typecheck:
name: Typecheck (mypy)
runs-on: ubuntu-latest
Expand All @@ -162,7 +161,7 @@ jobs:
- name: Run mypy
# No `continue-on-error` — mypy now exits zero on this repo (closes #29),
# so type errors must fail the job from here on.
run: mypy --ignore-missing-imports --no-strict-optional --pretty .
run: mypy --ignore-missing-imports --pretty .

# ── Secret scan: gitleaks ─────────────────────────────────────────────────
# Catches accidentally committed credentials. Runs over full git history
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ include = [
# and CI produce identical results.
[tool.mypy]
ignore_missing_imports = true
no_strict_optional = true
pretty = true
# Exclude virtual-env and build artefact directories so that `mypy .` from the
# repo root matches CI behaviour (CI runs in a clean runner without a local venv).
Expand Down
4 changes: 3 additions & 1 deletion services/workspace_listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,11 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:

headers = cd.get("fullConversationHeadersOnly") or []
has_bubbles = any(
bubble_map.get(h.get("bubbleId"))
bubble_map.get(bubble_id)
for h in headers
if isinstance(h, dict)
for bubble_id in [h.get("bubbleId")]
if isinstance(bubble_id, str)
)
if not has_bubbles:
continue
Expand Down
8 changes: 3 additions & 5 deletions services/workspace_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,10 @@ def _infer_workspace_name_from_context(workspace_path: str, workspace_id: str) -
except sqlite3.Error:
continue
for row in rows:
if not row or row[0] is None:
continue
try:
# sqlite3.Row supports string-key access at runtime when
# row_factory = sqlite3.Row is set on the connection (see
# _open_global_db); mypy's stub types Row as tuple[Any, ...]
# which only accepts SupportsIndex, hence the ignore.
ctx = json.loads(row["value"]) # type: ignore[call-overload]
ctx = json.loads(row[0])
except Exception:
continue
layouts = ctx.get("projectLayouts")
Expand Down
9 changes: 6 additions & 3 deletions services/workspace_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
if not isinstance(header, dict):
continue
bubble_id = header.get("bubbleId")
if not isinstance(bubble_id, str):
continue
bubble = bubble_map.get(bubble_id)
if not bubble:
continue
Expand Down Expand Up @@ -526,10 +528,11 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
if model_name_from_config and model_name_from_config != "default":
if not tab_meta:
tab_meta = {}
if not tab_meta.get("modelsUsed"):
models_used = tab_meta.get("modelsUsed")
if not isinstance(models_used, list):
tab_meta["modelsUsed"] = [model_name_from_config]
elif model_name_from_config not in tab_meta["modelsUsed"]:
tab_meta["modelsUsed"].insert(0, model_name_from_config)
elif model_name_from_config not in models_used:
models_used.insert(0, model_name_from_config)

tab = {
"id": composer_id,
Expand Down
1 change: 1 addition & 0 deletions tests/test_workspace_tabs_malformed_nested.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def test_diffs_appear_in_code_block_diffs_field(self) -> None:

tab = next((t for t in payload["tabs"] if t["id"] == "cmp-d"), None)
self.assertIsNotNone(tab)
assert tab is not None
self.assertTrue(tab["codeBlockDiffs"], "expected diffs on tab.codeBlockDiffs")

def test_diffs_do_not_appear_as_synthetic_bubbles(self) -> None:
Expand Down
Loading