Skip to content

chore: post-audit quick wins (name-based scores, CI coverage gate) - #3

Merged
satyamshivam13 merged 2 commits into
mainfrom
chore/post-audit-quick-wins
Jul 8, 2026
Merged

chore: post-audit quick wins (name-based scores, CI coverage gate)#3
satyamshivam13 merged 2 commits into
mainfrom
chore/post-audit-quick-wins

Conversation

@satyamshivam13

@satyamshivam13 satyamshivam13 commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Follow-up polish after the audit-remediation merge (#2).

Changes

  • M2 — remove score-ordering fragility. The ensemble/Binoculars analyzers addressed the fused primary score positionally (scores[0] / scores[1:]); reordering could silently break verdict math. Added AnalysisResult.get_score(name) and switched to name-based access (ENSEMBLE_SCORE_NAME, PRIMARY_SCORE_NAME, _voter_scores()). New test asserts the verdict is invariant to moving the primary score out of index 0.
  • CI coverage gate. Added --cov-fail-under=80 to the test job. Fast (not slow) suite currently covers 85%, so this is a real floor without breaking the build.
  • Refreshed .planning/codebase/ map to the post-remediation state (separate concern, already on main).

Validation

  • Fast suite: 231 passed, 85.04% coverage (clears the new gate).
  • M2 suites (incl. real-model Binoculars): 54 passed. Lint clean.

Not included (deferred)

  • mypy in CI — too slow locally (2min+ timeout) to add safely; needs a scoped config first.
  • v2.0.0 release/tag — best cut after this merges.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Score lookups now use names instead of list position, making results more reliable when score order changes.
    • Ensemble verdicts and explanations are now more consistent across reordered outputs.
  • Tests

    • Added coverage for score lookup and score-order robustness.
  • Documentation

    • Updated project docs to reflect the current app structure, runtime, testing, and integration guidance.
  • Chores

    • CI now enforces test coverage with a minimum threshold.

satyamshivam13 and others added 2 commits July 7, 2026 09:00
Regenerate .planning/codebase/ (7 docs) to reflect main after the audit-
remediation merge (PR #2): gpt2_app.py rename, new src/ui/ and src/evaluation/
packages, calibration.py + binoculars_analyzer.py, EnsembleConfig-driven
calibrated fusion, safetensors/patched-dep floors, CI, ~231 tests at ~92%
coverage, and the current remaining concerns (large-benchmark eval, revision
pinning, scores[0] ordering, numpy<2, py3.9 Docker base, CI mypy/coverage gates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M2 — remove positional score coupling:
The ensemble and Binoculars analyzers read the fused primary score via
result.scores[0] and the voter set via scores[1:], so reordering or inserting a
score would silently break verdict math. Add AnalysisResult.get_score(name) and
address scores by name:
- EnsembleAnalyzer.ENSEMBLE_SCORE_NAME constant + _voter_scores() helper.
- BinocularsAnalyzer.PRIMARY_SCORE_NAME constant.
New test asserts the ensemble verdict is unchanged when the primary score is
moved out of index 0.

CI — guard against coverage erosion:
Add --cov-fail-under=80 to the test job. The fast (not-slow) suite currently
covers 85% of src/, so this is a real floor without breaking the green build.

Tests: 54 (M2 suites incl. real-model binoculars) + get_score unit tests; fast
suite 231 passed at 85% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a name-based get_score lookup to AnalysisResult, migrates BinocularsAnalyzer and EnsembleAnalyzer to reference primary scores by named constants instead of positional indexing, adds regression/unit tests, enables an 80% coverage gate in CI, and rewrites .planning/codebase documentation files.

Changes

Named Score Lookup and Coverage Enforcement

Layer / File(s) Summary
AnalysisResult.get_score contract
src/models/result.py, tests/test_result_model.py
Adds Optional typing import and a get_score(name) method that returns the first matching DetectionScore by name or None, with tests for match, miss, and duplicate cases.
Binoculars named primary score
src/analyzers/binoculars_analyzer.py
Adds PRIMARY_SCORE_NAME constant, uses it when registering the primary score, and switches verdict determination from scores[0] to get_score(...).
Ensemble named primary score and voter filtering
src/analyzers/ensemble_analyzer.py, tests/test_ensemble_weighted_fusion.py
Adds ENSEMBLE_SCORE_NAME constant and _voter_scores helper, updates verdict determination and explanation generation to use named lookup and weight-filtered voters, and adds a reordering-robustness regression test.
CI coverage gate
.github/workflows/ci.yml
Adds coverage reporting for src with term-missing output and an 80% --cov-fail-under threshold to the pytest command.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Codebase Planning Documentation Refresh

Layer / File(s) Summary
Architecture and testing docs
.planning/codebase/ARCHITECTURE.md, .planning/codebase/TESTING.md
Condenses architecture sections (pattern overview, layers, data flow, abstractions, entry points, error handling) and rewrites testing documentation (framework, test areas, mocking, running commands, CI, gaps).
Concerns and conventions docs
.planning/codebase/CONCERNS.md, .planning/codebase/CONVENTIONS.md
Rewrites concerns to residual-only items post-remediation and condenses conventions into style, naming, patterns, error handling, security, comments, and commit guidance.
Integrations, stack, and structure docs
.planning/codebase/INTEGRATIONS.md, .planning/codebase/STACK.md, .planning/codebase/STRUCTURE.md
Rewrites integration scope, stack version/configuration details, and directory layout/naming/key-locations guidance.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant EnsembleAnalyzer
  participant AnalysisResult

  Caller->>EnsembleAnalyzer: _determine_verdict(result)
  EnsembleAnalyzer->>AnalysisResult: get_score(ENSEMBLE_SCORE_NAME)
  AnalysisResult-->>EnsembleAnalyzer: DetectionScore or None
  EnsembleAnalyzer->>EnsembleAnalyzer: _voter_scores(result)
  EnsembleAnalyzer-->>Caller: verdict
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: name-based score lookups and a CI coverage gate.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 chore/post-audit-quick-wins

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
src/models/result.py (1)

110-119: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add Google-style Args/Returns docstring blocks.

get_score is a public method with a parameter and return value but its docstring omits Args/Returns sections.

📝 Proposed docstring update
     def get_score(self, name: str) -> Optional[DetectionScore]:
-        """Return the first score with the given name, or None.
+        """Return the first score with the given name, or None.
 
-        Lets callers address scores by name instead of list position, so the
-        order in which scores are added is not a load-bearing contract.
-        """
+        Lets callers address scores by name instead of list position, so the
+        order in which scores are added is not a load-bearing contract.
+
+        Args:
+            name: The score name to look up.
+
+        Returns:
+            The first matching DetectionScore, or None if not found.
+        """

As per coding guidelines, "Public methods should include Google-style Args and Returns documentation blocks."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/models/result.py` around lines 110 - 119, The public method get_score in
Result is missing the required Google-style docstring sections. Update its
docstring to include explicit Args for name and a Returns block describing the
Optional[DetectionScore] result, while keeping the existing behavior and summary
intact.

Source: Coding guidelines

src/analyzers/ensemble_analyzer.py (2)

348-353: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Return type should be List[DetectionScore], not bare list.

_voter_scores loses element-type information for mypy/readers. Optional is already imported here; add List and annotate precisely.

🏷️ Proposed fix
-    def _voter_scores(self, result: AnalysisResult) -> list:
+    def _voter_scores(self, result: AnalysisResult) -> List[DetectionScore]:
         """Contributing analyzer scores: everything except the fused primary
         score, restricted to non-zero weight (so a disabled analyzer's row is
         never counted as a voter)."""
         return [s for s in result.scores if s.name != self.ENSEMBLE_SCORE_NAME and s.weight > 0]

(Requires List in the typing import line, alongside the existing Optional.)

As per coding guidelines, "Prefer from __future__ import annotations and typing imports such as Optional, List, Dict, and Tuple," which this repo runs mypy against in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/analyzers/ensemble_analyzer.py` around lines 348 - 353, The _voter_scores
method in EnsembleAnalyzer currently returns a bare list, which loses the
element type for mypy and readers. Update the typing in _voter_scores to return
List[DetectionScore], and add List to the existing typing imports near Optional
so the annotation is precise and consistent with the rest of the analyzer code.

Source: Coding guidelines


275-284: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace the positional score comment with name-based wording

# Add ensemble score (always index 0 by contract). is outdated now that ENSEMBLE_SCORE_NAME is the lookup key; keep the comment aligned so it doesn’t suggest positional access.

📝 Proposed fix
-        # Add ensemble score (always index 0 by contract).
+        # Add the fused primary score, addressed by name (ENSEMBLE_SCORE_NAME),
+        # not by list position.
         result.add_score(
             DetectionScore(
                 name=self.ENSEMBLE_SCORE_NAME,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/analyzers/ensemble_analyzer.py` around lines 275 - 284, Update the
outdated comment in EnsembleAnalyzer around the DetectionScore addition so it no
longer implies the ensemble score is accessed by position; the code now uses
ENSEMBLE_SCORE_NAME as the lookup key. Keep the comment aligned with the current
contract by describing name-based identification instead of “always index 0,”
and make the wording consistent with add_score and _interpret_ensemble_score
usage.
.github/workflows/ci.yml (1)

54-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Coverage gate diverges from the Makefile's test target.

The Makefile's canonical make test command (PYTHONPATH=src $(PYTHON) -m pytest tests/ -v --cov=src --cov-report=html --cov-report=term-missing) has no --cov-fail-under, so contributors running tests locally won't hit the same 80% gate CI now enforces. Consider adding the same --cov-fail-under=80 to the Makefile target (or centralizing the threshold in pytest.ini/.coveragerc/pyproject.toml so both consumers stay in sync).

♻️ Suggested Makefile alignment
 test: ## Run tests with coverage
-	PYTHONPATH=src $(PYTHON) -m pytest tests/ -v --cov=src --cov-report=html --cov-report=term-missing
+	PYTHONPATH=src $(PYTHON) -m pytest tests/ -v --cov=src --cov-report=html --cov-report=term-missing --cov-fail-under=80
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 54, The coverage threshold is enforced in
CI but not in the canonical local test path, so `make test` and the workflow are
out of sync. Update the Makefile `test` target to include the same
`--cov-fail-under=80` used by the CI pytest command, or move the threshold into
shared pytest coverage config so both `pytest` and the workflow consume the same
setting. Use the `test` target and the CI pytest invocation as the two places to
align.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 54: The coverage threshold is enforced in CI but not in the canonical
local test path, so `make test` and the workflow are out of sync. Update the
Makefile `test` target to include the same `--cov-fail-under=80` used by the CI
pytest command, or move the threshold into shared pytest coverage config so both
`pytest` and the workflow consume the same setting. Use the `test` target and
the CI pytest invocation as the two places to align.

In `@src/analyzers/ensemble_analyzer.py`:
- Around line 348-353: The _voter_scores method in EnsembleAnalyzer currently
returns a bare list, which loses the element type for mypy and readers. Update
the typing in _voter_scores to return List[DetectionScore], and add List to the
existing typing imports near Optional so the annotation is precise and
consistent with the rest of the analyzer code.
- Around line 275-284: Update the outdated comment in EnsembleAnalyzer around
the DetectionScore addition so it no longer implies the ensemble score is
accessed by position; the code now uses ENSEMBLE_SCORE_NAME as the lookup key.
Keep the comment aligned with the current contract by describing name-based
identification instead of “always index 0,” and make the wording consistent with
add_score and _interpret_ensemble_score usage.

In `@src/models/result.py`:
- Around line 110-119: The public method get_score in Result is missing the
required Google-style docstring sections. Update its docstring to include
explicit Args for name and a Returns block describing the
Optional[DetectionScore] result, while keeping the existing behavior and summary
intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 49abec29-cf32-4a95-a483-c71a9fa501a3

📥 Commits

Reviewing files that changed from the base of the PR and between e85e393 and ef8a925.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • .planning/codebase/ARCHITECTURE.md
  • .planning/codebase/CONCERNS.md
  • .planning/codebase/CONVENTIONS.md
  • .planning/codebase/INTEGRATIONS.md
  • .planning/codebase/STACK.md
  • .planning/codebase/STRUCTURE.md
  • .planning/codebase/TESTING.md
  • src/analyzers/binoculars_analyzer.py
  • src/analyzers/ensemble_analyzer.py
  • src/models/result.py
  • tests/test_ensemble_weighted_fusion.py
  • tests/test_result_model.py

@satyamshivam13
satyamshivam13 merged commit b709533 into main Jul 8, 2026
10 checks passed
@satyamshivam13
satyamshivam13 deleted the chore/post-audit-quick-wins branch July 8, 2026 12:36
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.

1 participant