Follow-up to #49 (closed by #62). #49 cleared every error the deps-less
mypy check could see and removed the last [[tool.mypy.overrides]] entry, so
mypy . is now clean under the required lint gate.
But that gate runs mypy without project dependencies — with
ignore_missing_imports, every third-party import (numpy, pillow, anthropic, …)
collapses to Any, hiding all type errors that involve those libraries. #62
added a separate mypy-strict CI job that runs uv run mypy . with the
real packages installed (the check contributors get locally per
CONTRIBUTING.md). It is continue-on-error: true for now.
That job currently reports 236 errors in 22 files — none of them
regressions from #62; they are pre-existing mismatches the deps-less gate could
never see. This issue tracks clearing them so mypy-strict can be flipped to
a required gate (the open task carried over from #49).
Breakdown by error code
| Code |
Count |
union-attr |
154 |
operator |
25 |
arg-type |
22 |
assignment |
15 |
attr-defined |
11 |
list-item |
4 |
return-value |
3 |
typeddict-item |
1 |
index |
1 |
Breakdown by file
| File |
Errors |
gently/harness/detection/verifier.py |
60 |
gently/hardware/dispim/claude_client.py |
48 |
gently/analysis/steps.py |
16 |
scripts/auto_label_examples.py |
12 |
gently/organisms/celegans/developmental_tracker.py |
12 |
gently/harness/detection/queue.py |
12 |
gently/app/agent.py |
12 |
gently/core/imaging.py |
10 |
gently/app/tools/focus_tools.py |
10 |
gently/detection.py |
9 |
scripts/projection_explorer.py |
8 |
scripts/watch_devices.py |
6 |
gently/analysis/core.py |
6 |
gently/ui/web/server.py + plots.py |
3 + 3 |
(remaining: watch_position, harness/state, recreate_session_videos, embryo_marker, sam_detection, calibration_tools, analysis/focus) |
1–2 each |
Candidate clusters (each a single-PR phase, à la #48 Phase 3)
-
Anthropic ContentBlock union narrowing — ~154 errors / 7 files (the big one).
Almost every union-attr error is the same root cause: reading .text off
response.content[0] where the SDK types the block as
TextBlock | ThinkingBlock | ToolUseBlock | … (12-member union). Each call
site emits ~11 errors. A single shared helper (e.g.
def _block_text(block) -> str with an isinstance(block, TextBlock) guard,
or filtering [b for b in content if isinstance(b, TextBlock)]) clears the
whole cluster. This one has real runtime-bug surface: if a response's
first block is ever a ToolUseBlock/ThinkingBlock, block.text raises
AttributeError today. Concentrated in verifier.py (55), claude_client.py
(44), and auto_label_examples.py / developmental_tracker.py / queue.py /
agent.py / steps.py (11 each).
-
numpy list vs ndarray in focus-curve code — ~25 errors.
analysis/core.py, app/tools/focus_tools.py, harness/state.py: locals
annotated list[float] are reassigned to np.asarray(...) and then
.min()/.max() / fit_focus_curve(...) are called on them. Fix the
annotation (or keep them ndarrays). Mostly benign at runtime.
-
numpy scalar operands — ~10 operator errors.
detection.py, imaging.py: arithmetic on np.clip/np.min returns typed
as SupportsDunderLT[Any]. Annotation-level; benign.
-
pillow ImageFont.truetype union — embryo_marker.py, recreate_session_videos.py.
Returns FreeTypeFont | ImageFont; narrow or annotate.
-
bytes vs str in watch scripts — scripts/watch_devices.py (6), watch_position.py (2).
subprocess output handled as both bytes and str — worth checking for a
real decode bug, not just an annotation.
Tasks
🤖 Generated with Claude Code
Follow-up to #49 (closed by #62). #49 cleared every error the deps-less
mypy check could see and removed the last
[[tool.mypy.overrides]]entry, somypy .is now clean under the requiredlintgate.But that gate runs mypy without project dependencies — with
ignore_missing_imports, every third-party import (numpy, pillow, anthropic, …)collapses to
Any, hiding all type errors that involve those libraries. #62added a separate
mypy-strictCI job that runsuv run mypy .with thereal packages installed (the check contributors get locally per
CONTRIBUTING.md). It is
continue-on-error: truefor now.That job currently reports 236 errors in 22 files — none of them
regressions from #62; they are pre-existing mismatches the deps-less gate could
never see. This issue tracks clearing them so
mypy-strictcan be flipped toa required gate (the open task carried over from #49).
Breakdown by error code
union-attroperatorarg-typeassignmentattr-definedlist-itemreturn-valuetypeddict-itemindexBreakdown by file
gently/harness/detection/verifier.pygently/hardware/dispim/claude_client.pygently/analysis/steps.pyscripts/auto_label_examples.pygently/organisms/celegans/developmental_tracker.pygently/harness/detection/queue.pygently/app/agent.pygently/core/imaging.pygently/app/tools/focus_tools.pygently/detection.pyscripts/projection_explorer.pyscripts/watch_devices.pygently/analysis/core.pygently/ui/web/server.py+plots.pywatch_position,harness/state,recreate_session_videos,embryo_marker,sam_detection,calibration_tools,analysis/focus)Candidate clusters (each a single-PR phase, à la #48 Phase 3)
Anthropic
ContentBlockunion narrowing — ~154 errors / 7 files (the big one).Almost every
union-attrerror is the same root cause: reading.textoffresponse.content[0]where the SDK types the block asTextBlock | ThinkingBlock | ToolUseBlock | …(12-member union). Each callsite emits ~11 errors. A single shared helper (e.g.
def _block_text(block) -> strwith anisinstance(block, TextBlock)guard,or filtering
[b for b in content if isinstance(b, TextBlock)]) clears thewhole cluster. This one has real runtime-bug surface: if a response's
first block is ever a
ToolUseBlock/ThinkingBlock,block.textraisesAttributeErrortoday. Concentrated inverifier.py(55),claude_client.py(44), and
auto_label_examples.py/developmental_tracker.py/queue.py/agent.py/steps.py(11 each).numpy
listvsndarrayin focus-curve code — ~25 errors.analysis/core.py,app/tools/focus_tools.py,harness/state.py: localsannotated
list[float]are reassigned tonp.asarray(...)and then.min()/.max()/fit_focus_curve(...)are called on them. Fix theannotation (or keep them ndarrays). Mostly benign at runtime.
numpy scalar operands — ~10
operatorerrors.detection.py,imaging.py: arithmetic onnp.clip/np.minreturns typedas
SupportsDunderLT[Any]. Annotation-level; benign.pillow
ImageFont.truetypeunion —embryo_marker.py,recreate_session_videos.py.Returns
FreeTypeFont | ImageFont; narrow or annotate.bytes vs str in watch scripts —
scripts/watch_devices.py(6),watch_position.py(2).subprocess output handled as both
bytesandstr— worth checking for areal decode bug, not just an annotation.
Tasks
ContentBlocknarrowing fix first (biggest cluster + only one with runtime-bug risk)mypy-strictafter each, drive the count to 0mypy-strictfromcontinue-on-error: trueto a required gate🤖 Generated with Claude Code