Skip to content

fix(para): stop suggest_archive dividing by a non-positive inactive_days#1679

Merged
curdriceaurora merged 5 commits into
mainfrom
jovial-shaw-87c9ae
Jul 25, 2026
Merged

fix(para): stop suggest_archive dividing by a non-positive inactive_days#1679
curdriceaurora merged 5 commits into
mainfrom
jovial-shaw-87c9ae

Conversation

@curdriceaurora

@curdriceaurora curdriceaurora commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Description

PARAFileMover.suggest_archive computed archive confidence as:

confidence = min(0.95, 0.5 + (days_inactive / (inactive_days * 3)))

inactive_days is an unvalidated int, and inactive_days=0 is a reasonable
caller intent — "archive everything regardless of age". It raised
ZeroDivisionError: float division by zero.

Negative thresholds were broken too, in a second way: days_inactive >= inactive_days
passes for every file, then the negative divisor drives confidence below zero and
MoveSuggestion.__post_init__ rejects it
(ValueError: confidence must be between 0.0 and 1.0, got -99.5 for a 300-day-old
file at inactive_days=-1).

Fix: saturate confidence at 0.95 when inactive_days <= 0. A non-positive
threshold selects every file, so the age ratio carries no signal; one branch covers
both failure modes without adding a new error path. The docstring now states that
0 or less means "archive everything regardless of age" at maximum confidence.

Why not reject inactive_days < 1 with ValueError: an existing test already
calls suggest_archive(src, inactive_days=0)
(tests/methodologies/para/test_file_mover.py, test_file_stat_error) and only
escapes the crash because it monkeypatches stat to raise. The suite already treats
0 as a legitimate call, and saturating preserves the "archive everything" intent.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Two regression tests added to TestSuggestArchive in
    tests/methodologies/para/test_file_mover.py:
    test_zero_inactive_days_archives_everything and
    test_negative_inactive_days_archives_everything. Both were written first and
    confirmed failing against the previous code with exactly the two errors above.
  • pytest tests/methodologies/para/ tests/integration/test_para_file_mover.py
    — 702 passed.
  • ruff check, ruff format --check, mypy clean on both changed files.
  • pre-commit run --files <both files> — all hooks pass. diff-cover was
    skipped locally (it runs the full suite single-threaded and times out); both
    new branches are directly exercised by the tests above, and CI runs the gate.

Notes for the reviewer

  • No production callers. suggest_archive is referenced only inside its own
    module and in tests (grepped .py/.md/.json/.yaml/.toml repo-wide), so
    nothing currently passes a computed or user-supplied threshold. This is a public
    library API whose contract is now safe across the full int range.
  • The safe_walk migration has not landed for this file. The scan on line ~323
    is still directory.rglob("*") on main (f72ffd74); core.path_guard.safe_walk
    adoption for the PARA movers is in flight in security(traversal): migrate 13 rglob call sites to safe_walk #1676. This change is independent of
    which scan API is used and should not conflict beyond trivial context.
  • The reasoning string reads File has not been modified in 0 days (threshold: 0 days)
    at the saturated threshold, which seemed accurate enough to leave as-is.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (docstring Args)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Archive suggestions now handle zero or negative inactivity thresholds correctly.
    • Files are selected for archiving with a confidence score of 0.95 when the threshold is non-positive.
  • Tests

    • Added coverage for zero and negative inactivity thresholds, including integration scenarios.

PARAFileMover.suggest_archive computed confidence as
min(0.95, 0.5 + days_inactive / (inactive_days * 3)), which raised
ZeroDivisionError for inactive_days=0 -- a reasonable caller intent
meaning "archive everything regardless of age". Negative thresholds
were broken too: every file passes the age check, then the negative
divisor drives confidence below zero and MoveSuggestion.__post_init__
rejects it with ValueError.

Saturate confidence at 0.95 when inactive_days <= 0 instead. A
non-positive threshold selects every file, so the age ratio carries no
signal, and one branch covers both failure modes without adding a new
error path. Rejecting 0 with ValueError was the alternative, but an
existing test already calls suggest_archive(inactive_days=0) and only
escapes the crash by monkeypatching stat.

Adds regression tests for the zero and negative thresholds; both were
confirmed failing against the previous code.

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

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@curdriceaurora, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 666e99a5-79e3-4b93-bcae-b2ecacc90a5b

📥 Commits

Reviewing files that changed from the base of the PR and between 2effd28 and e941f8a.

📒 Files selected for processing (3)
  • src/file_organizer/methodologies/para/ai/file_mover.py
  • tests/integration/test_para_file_mover.py
  • tests/methodologies/para/test_file_mover.py
📝 Walkthrough

Walkthrough

suggest_archive now treats non-positive inactive_days values as matching all files and assigns confidence 0.95. Integration and unit tests cover zero and negative thresholds.

Changes

Archive threshold handling

Layer / File(s) Summary
Handle non-positive archive thresholds
src/file_organizer/methodologies/para/ai/file_mover.py, tests/methodologies/para/test_file_mover.py, tests/integration/test_para_file_mover.py
inactive_days <= 0 now produces archive suggestions with confidence 0.95; tests verify zero and negative thresholds, archive categorization, and confidence values.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: handling non-positive inactive_days in suggest_archive.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jovial-shaw-87c9ae

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.

Claude and others added 3 commits July 25, 2026 01:45
The diff-coverage gate failed at 66.7%: line 346 (the saturated
confidence branch) was uncovered. CI generates coverage.xml from
`pytest tests/ -m "ci and not benchmark"`, and the two new regression
tests carried only the class-level `unit` marker, so they never ran in
that selection. The positive-threshold branch was covered incidentally
by the ci-marked tests/integration/test_para_file_mover.py.

Add `@pytest.mark.ci` to both regression tests. Verified with the gate's
own command against a ci-selected coverage run: 3 changed lines, 0
missing, 100%. Both tests are Windows-safe (tmp_path + os.utime only),
which matters now that `ci` also pulls them into test-windows
(-m "ci or smoke").

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-file integration coverage floor failed: file_mover.py dropped to
84.7%, below its 85% floor. The floor is measured over
`pytest tests/ -m "integration or conformance"`, and the fix added two
statements plus two branch arcs there, of which the saturated-confidence
branch was unreachable from that pool -- the unit-suite regressions do
not run in it.

Add the zero-threshold case to tests/integration/test_para_file_mover.py
(module-marked integration + ci), which covers all four added units.

Verified with the gate's own arithmetic
(covered_lines + covered_branches) / (num_statements + num_branches):
84.69% -> 85.71%. The 84.69% reproduces CI's reported 84.7% exactly, so
the local measurement tracks the gate. Coverage over the full pool is
monotonically >= this subset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@curdriceaurora
curdriceaurora marked this pull request as ready for review July 25, 2026 06:09
@curdriceaurora

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/file_organizer/methodologies/para/ai/file_mover.py`:
- Around line 314-316: Update the file-selection predicate in the method
handling inactive-file suggestions so inactive_days <= 0 bypasses the
days_inactive threshold and includes every eligible file, while preserving the
existing age check for positive thresholds. Add a regression test covering a
future modification time with a non-positive inactive_days value and verify it
is selected.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 962b1c5c-13d9-4f7a-a35b-1ef5eca384da

📥 Commits

Reviewing files that changed from the base of the PR and between 6240019 and 2effd28.

📒 Files selected for processing (3)
  • src/file_organizer/methodologies/para/ai/file_mover.py
  • tests/integration/test_para_file_mover.py
  • tests/methodologies/para/test_file_mover.py

Comment thread src/file_organizer/methodologies/para/ai/file_mover.py
…nfidence

CodeRabbit review on #1679: the docstring promises inactive_days <= 0
archives every file, but the selection predicate still read
`days_inactive >= inactive_days`. A file whose mtime is in the future --
clock skew, or an archive extracted with a bogus timestamp -- has a
negative days_inactive and was silently excluded. Verified: a file dated
30 days ahead returned 0 suggestions at inactive_days=0.

Short-circuit the predicate on the non-positive threshold so selection
matches the documented contract and the confidence branch beside it.

Also clamp the reasoning day count at 0. That string is now reachable
with a negative days_inactive, and "has not been modified in -30 days"
is nonsense; positive-threshold output is unchanged.

Regression tests in both gate pools: parametrized (0, -1) in the unit
suite, plus an integration case. Both guards mutation-tested -- reverting
either the predicate or the clamp fails all three tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@curdriceaurora
curdriceaurora merged commit f262c48 into main Jul 25, 2026
78 checks passed
@curdriceaurora
curdriceaurora deleted the jovial-shaw-87c9ae branch July 25, 2026 12:30
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