Skip to content

chore(typing): promote pyright reportUnknown* warnings to errors (#165)#166

Merged
ToaruPen merged 5 commits into
mainfrom
feat/issue-165-pyright-strict
Apr 8, 2026
Merged

chore(typing): promote pyright reportUnknown* warnings to errors (#165)#166
ToaruPen merged 5 commits into
mainfrom
feat/issue-165-pyright-strict

Conversation

@ToaruPen
Copy link
Copy Markdown
Owner

@ToaruPen ToaruPen commented Apr 8, 2026

Summary

pyrightconfig.json was running typeCheckingMode=strict while explicitly downgrading reportUnknownVariableType and reportUnknownArgumentType to warning, hiding 116 latent type-information gaps. This PR restores the strict-mode default and adds explicit annotations / casts to fix every warning that surfaced.

Changes

  • pyrightconfig.json: drop the two warning overrides (now strict default = error)
  • ~30 files: add list/dict/tuple element type annotations, dataclass field annotations, cast() at boundary calls
  • No logic changes, no new helpers, no refactors
  • Restored 2 # noqa: BLE001 markers and reformatted 5 files in fixup commit (6cb1cab)

Stats

  • Files modified: 30 source files + pyrightconfig.json
  • cast(...) additions: ~64
  • New cast imports: 15
  • TypedDict additions: 0 (annotations alone were sufficient)

Test plan

  • uv run ruff format --check daemon tests scripts — pass
  • uv run ruff check daemon tests scripts — pass
  • uv run mypy --strict daemon/src tests — pass
  • uv run pyright daemon/src0 errors, 0 warnings, 0 informations
  • uv run pytest tests/ — 1230 passed, 11 deselected
  • .claude/.simplify-done marker created
  • ADR decision recorded (adr_required: false)

Why this matters

Strict mode is meaningless if individual checks are silently relaxed. The downgrade was a Trojan horse — it allowed the strict label to stay green while letting unknown-typed values flow through tool argument paths, audit redactor, harness loops, and more. Restoring the defaults makes future regressions visible at PR time instead of accumulating.

Closes #165

🤖 Generated with Claude Code

Summary by CodeRabbit

リリースノート

  • 保守作業

    • JSON/配列/辞書の取り扱いで明示的な型キャストを導入し、型安全性と静的解析の整合性を強化しました。
    • デフォルト値の初期化を明示的に型化して型チェックの信頼性を向上させました。
  • ドキュメント

    • 型チェック方針の変更を記録するADRを追加しました。
  • テスト

    • 型変換関連の振る舞いを検証する単体テストを追加しました。

ToaruPen and others added 2 commits April 8, 2026 13:08
pyrightconfig.json was running typeCheckingMode=strict but explicitly downgraded reportUnknownVariableType and reportUnknownArgumentType to warnings, hiding 116 latent type-information gaps. This restores the strict-mode default and adds explicit annotations / casts / TypedDicts to fix every warning that surfaced.

Net effect:
- pyrightconfig.json: drop the two warning overrides
- ~30 files: add list/dict/tuple element type annotations, dataclass field type annotations, cast() at boundary calls, TypedDict where dict[str, Any] was load-bearing
- No logic changes, no new helpers, no refactors

Tests still pass (sandbox-known port-bind exception remains).

Closes #165
Follow-up to f1fcfab. The previous commit accidentally:
- Stripped two `# noqa: BLE001` comments from intentional broad
  `except Exception:` blocks (daemon/src/daemon/app.py speech playback,
  memory/review/commands.py mem0 export retry path) → ruff lint failed
- Left five files unformatted: audit/local_sink.py,
  memory/mem0_bridge/backfill.py, memory/read/search.py,
  memory/retention/rollup_generator.py, memory/review/commands.py
  → ruff format --check failed

Restoring the noqa markers and reformatting brings ruff format/check
back to clean. pyright still 0/0/0 and mypy strict still passes.

Refs #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: efea6205-c286-4184-bbd1-0d77d99891e5

📥 Commits

Reviewing files that changed from the base of the PR and between a6124e4 and cb8b498.

📒 Files selected for processing (1)
  • tests/memory/mem0_bridge/test_backfill.py

📝 Walkthrough

Walkthrough

多数のモジュールで JSON / list / dict や dataclass/Pydantic の空リテラルに対し typing.cast と明示的な型付き default_factory を導入し、型を絞った。制御フロー・公開 API・振る舞いに変更はない。

Changes

Cohort / File(s) Summary
LLM / adapters
daemon/src/daemon/adapters/camera/onvif_client.py, daemon/src/daemon/adapters/llm/event_parser.py, daemon/src/daemon/adapters/llm/openai_transport.py
json/mapping/list を処理する前に typing.cast(...) で中間変数を型絞り。ロジックは不変。
App / Audit / Redaction
daemon/src/daemon/app.py, daemon/src/daemon/audit/langfuse_export.py, daemon/src/daemon/audit/local_sink.py, daemon/src/daemon/audit/redactor.py
yaml/json の戻り値や dataclass の default_factory を cast で明示化。公開署名や挙動は変わらない。
Config loader
daemon/src/daemon/config/loader.py
yaml.safe_load(...) or {} の結果を cast("dict[str, object]", ...) してから Pydantic に渡すよう変更。
Contracts / Pydantic fields
daemon/src/daemon/contracts/adapters.py, daemon/src/daemon/contracts/archive.py, daemon/src/daemon/contracts/audio.py, daemon/src/daemon/contracts/config_docs.py, daemon/src/daemon/contracts/memory.py
空の list/dict の default_factory を lambda+cast(...) に統一し、thaw/coerce 周りで中間変数を cast して型を狭めた。
Core / Harness / Tooling
daemon/src/daemon/core/logging.py, daemon/src/daemon/harness/tool_loop.py, daemon/src/daemon/harness/tool_registry.py
sanitize/normalize/coerce 処理で list/dict を先に cast してから反復。ToolRegistry.dispatch で引数を dict[str, object] にキャストして渡すよう変更。
Memory subsystem
daemon/src/daemon/memory/mem0_bridge/backfill.py, daemon/src/daemon/memory/mem0_bridge/store.py, daemon/src/daemon/memory/read/search.py, daemon/src/daemon/memory/retention/rollup_generator.py, daemon/src/daemon/memory/review/commands.py, daemon/src/daemon/memory/write/embeddings.py, daemon/src/daemon/memory/write/extractor.py, daemon/src/daemon/memory/write/tool.py
外部入力/JSON の list・dict を cast で型狭め。例外ハンドラの変数束縛などの軽微修正。動作は維持。
Pipeline / Security / Tools
daemon/src/daemon/pipeline/latency_metrics.py, daemon/src/daemon/security/websocket_auth.py, daemon/src/daemon/tools/evaluate_detection.py
フィールドの default_factory を lambda+cast に変更してランタイムの空リスト/辞書の型を明示化。
Docs / Pyright config / Tests
pyrightconfig.json, docs/adr/decision-log.md, docs/adr/decisions/2026-04-08-promote-pyright-reportunknown-warnings-to-errors-and-fix-116-latent-issues.md, tests/memory/mem0_bridge/test_backfill.py
Pyright 設定の整形と ADR 追加。_float_or_none の単体テストを拡充(None, Decimal, float, inf, -inf, NaN を含む)。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR のタイトルは pyright の reportUnknown* warnings を errors に昇格させるという主要な変更を正確かつ簡潔に表現しており、主な目的と一致している。
Linked Issues check ✅ Passed PR は #165 の全ての要件を満たしている: pyrightconfig.json の設定変更、116 の警告修正(cast/annotation で対応)、0 errors 達成、全 CI gate(ruff、mypy --strict、pyright、pytest)合格。
Out of Scope Changes check ✅ Passed ドキュメント更新(ADR 記録)と test 拡張(_float_or_none テスト)は全て scope 内。変更は型注釈と cast の追加に限定され、ロジック・機能変更なし。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-165-pyright-strict

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.17.0)
tests/memory/mem0_bridge/test_backfill.py

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

🔧 Semgrep (1.157.0)
tests/memory/mem0_bridge/test_backfill.py

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@daemon/src/daemon/contracts/memory.py`:
- Line 38: The Field default_factory for matches uses an unnecessary cast:
replace the lambda cast pattern in the matches: list[MemoryMatch] =
Field(default_factory=lambda: cast("list[MemoryMatch]", [])) with a simple
default_factory=list to match other fields and remove complexity; update the
declaration for matches (type MemoryMatch) to use Field(default_factory=list)
and remove the cast/imports that become unused.

In `@daemon/src/daemon/memory/mem0_bridge/backfill.py`:
- Line 274: Replace the invalid Python2-style exception clause in backfill.py by
changing the line "except TypeError, ValueError:" to the Python3-compatible
tuple form "except (TypeError, ValueError):" so the file parses correctly;
locate the clause in daemon/src/daemon/memory/mem0_bridge/backfill.py (the
except block handling TypeError and ValueError) and update the syntax only,
preserving the existing handler body and indentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ab1bec07-23b5-40fa-a536-0ed487b56c9b

📥 Commits

Reviewing files that changed from the base of the PR and between f7bfa75 and 6cb1cab.

📒 Files selected for processing (30)
  • daemon/src/daemon/adapters/camera/onvif_client.py
  • daemon/src/daemon/adapters/llm/event_parser.py
  • daemon/src/daemon/adapters/llm/openai_transport.py
  • daemon/src/daemon/app.py
  • daemon/src/daemon/audit/langfuse_export.py
  • daemon/src/daemon/audit/local_sink.py
  • daemon/src/daemon/audit/redactor.py
  • daemon/src/daemon/config/loader.py
  • daemon/src/daemon/contracts/adapters.py
  • daemon/src/daemon/contracts/archive.py
  • daemon/src/daemon/contracts/audio.py
  • daemon/src/daemon/contracts/config_docs.py
  • daemon/src/daemon/contracts/memory.py
  • daemon/src/daemon/core/logging.py
  • daemon/src/daemon/harness/tool_loop.py
  • daemon/src/daemon/harness/tool_registry.py
  • daemon/src/daemon/memory/mem0_bridge/backfill.py
  • daemon/src/daemon/memory/mem0_bridge/store.py
  • daemon/src/daemon/memory/read/search.py
  • daemon/src/daemon/memory/retention/rollup_generator.py
  • daemon/src/daemon/memory/review/commands.py
  • daemon/src/daemon/memory/write/embeddings.py
  • daemon/src/daemon/memory/write/extractor.py
  • daemon/src/daemon/memory/write/tool.py
  • daemon/src/daemon/pipeline/latency_metrics.py
  • daemon/src/daemon/security/websocket_auth.py
  • daemon/src/daemon/tools/evaluate_detection.py
  • docs/adr/decision-log.md
  • docs/adr/decisions/2026-04-08-promote-pyright-reportunknown-warnings-to-errors-and-fix-116-latent-issues.md
  • pyrightconfig.json

Comment thread daemon/src/daemon/contracts/memory.py Outdated
Comment thread daemon/src/daemon/memory/mem0_bridge/backfill.py Outdated
Address CodeRabbit findings on PR #166:
- contracts/memory.py: simplify
  `Field(default_factory=lambda: cast("list[MemoryMatch]", []))` to
  `Field(default_factory=list[MemoryMatch])`. The generic-alias factory
  satisfies pyright strict without the redundant cast/lambda wrapper
  and removes the unused `cast` import.
- memory/mem0_bridge/backfill.py: restore the parenthesized
  `except (TypeError, ValueError):` form. ruff 0.15.1's Python 3.14
  formatter strips the parens to `except TypeError, ValueError:` —
  syntactically valid in 3.14 but unidiomatic. Re-parenthesizing with
  an `as _exc` binding (unused; required because parens-only is what
  ruff drops) keeps the conventional reading.

Refs #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
daemon/src/daemon/memory/mem0_bridge/backfill.py (1)

267-275: ⚠️ Potential issue | 🟡 Minor

None、float、Decimalの入力に対する明示的なテストケースを追加してください

Exception path(line 273-275)はすでに test_float_or_none_with_unparseable_string でテスト済みです。ただし以下のケースが不足しています:

  • None 入力(line 268の早期リターンをテストする明示的なケースなし)
  • float 入力(isinstance チェックでカバーされているが、テストケースなし)
  • Decimal 入力(isinstance チェックでカバーされているが、テストケースなし)

各入力型に対して明示的なテストを追加してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@daemon/src/daemon/memory/mem0_bridge/backfill.py` around lines 267 - 275, Add
explicit unit tests for the _float_or_none function to cover the missing input
types: create tests named like test_float_or_none_with_none (assert
_float_or_none(None) is None), test_float_or_none_with_float (assert
_float_or_none(1.23) == 1.23), and test_float_or_none_with_decimal (use
Decimal('1.23') and assert the return equals float(Decimal('1.23'))). Keep the
existing test_float_or_none_with_unparseable_string unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@daemon/src/daemon/memory/mem0_bridge/backfill.py`:
- Around line 267-275: Add explicit unit tests for the _float_or_none function
to cover the missing input types: create tests named like
test_float_or_none_with_none (assert _float_or_none(None) is None),
test_float_or_none_with_float (assert _float_or_none(1.23) == 1.23), and
test_float_or_none_with_decimal (use Decimal('1.23') and assert the return
equals float(Decimal('1.23'))). Keep the existing
test_float_or_none_with_unparseable_string unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02d8d229-274f-4320-98ef-b79920ac1dda

📥 Commits

Reviewing files that changed from the base of the PR and between 6cb1cab and 0f869c4.

📒 Files selected for processing (2)
  • daemon/src/daemon/contracts/memory.py
  • daemon/src/daemon/memory/mem0_bridge/backfill.py

coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 8, 2026
Address CodeRabbit outside-diff comment on PR #166: the existing
suite covered bool, str, unparseable str, and int but left the
None early-return and the float/Decimal isinstance branches without
explicit assertions. Add three small parameterless tests so each
input type has a named regression case.

Refs #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/memory/mem0_bridge/test_backfill.py`:
- Around line 382-397: Add tests to cover special float values and Decimal→float
precision edge cases for _float_or_none: add a parametrized test (e.g.,
test_float_or_none_with_special_floats) that passes float('inf'), float('-inf'),
and float('nan') and asserts identity for infinities and NaN-preserving behavior
(use math.isnan for NaN check), and add a test (e.g.,
test_float_or_none_decimal_precision_loss) that converts a very high-precision
Decimal and asserts the result equals pytest.approx(float(high_precision));
reference the existing _float_or_none import in
tests/memory/mem0_bridge/test_backfill.py and reuse pytest.mark.parametrize and
math for the special-value assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0732206b-f464-4a36-affe-aff4035f32b4

📥 Commits

Reviewing files that changed from the base of the PR and between 0f869c4 and a6124e4.

📒 Files selected for processing (1)
  • tests/memory/mem0_bridge/test_backfill.py

Comment thread tests/memory/mem0_bridge/test_backfill.py
…_float_or_none

Address CodeRabbit nitpick on PR #166: extend the new _float_or_none
suite with the special-value branches the implementation passes
through unchanged:
- ±inf via parametrized assert (round-trip equality)
- NaN via math.isnan (NaN != NaN so direct compare is unsafe)
- 30-digit Decimal via pytest.approx (verifies float() loss is bounded)

Refs #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ToaruPen ToaruPen merged commit 5ae983b into main Apr 8, 2026
7 checks passed
@ToaruPen ToaruPen deleted the feat/issue-165-pyright-strict branch April 8, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(typing): promote pyright reportUnknown* warnings to errors and fix 116 latent issues

1 participant