Skip to content

[2G-Bug-03]: Credit habit_completions rows toward evaluate_frequency_step_down "Already done" rate on expired nudges - #197

Merged
WilliM233 merged 1 commit into
developfrom
feature/2G-Bug-03-step-down-hybrid-credit
Apr 19, 2026
Merged

WilliM233 merged 1 commit into
developfrom
feature/2G-Bug-03-step-down-hybrid-credit

Conversation

@WilliM233

Copy link
Copy Markdown
Owner

Verification

  • ruff check . — ✅ Pass (All checks passed!)
  • pytest -v — ✅ Pass (1299 passed, 4 skipped)
  • Migration applied locally on brain3-dev: N/A (no schema change)
  • Postgres-backed test confirmed: N/A (pure ORM logic; no Postgres-specific behavior)

Ran locally against develop HEAD at 2d0a13a immediately before opening this PR.

Summary

evaluate_frequency_step_down now credits a same-date habit_completions row when an expired nudge in the LIMIT-14 window has no response. This mirrors the D5 fix from #184 on the step-down surface so users whose habits complete via routine cascade (or any non-nudge path) can actually step down from daily nudges — the ADHD quality lens again: multiple low-friction completion paths all feel like "I did it", and scaffolding should fade regardless of which channel recorded the completion.

Explicit responses still win. "Already done" counts on its own (back-compat preserved). An explicit non-"Already done" response (e.g., "Skip today") is never overridden by a completion row.

The completion range is bounded by the scheduled_at span of the 14 most recent notifications, so it adapts naturally to the habit's current frequency — a daily habit looks at ~2 weeks of completions; a weekly habit looks at ~14 weeks. FrequencyStepResult shape unchanged; the denominator stays at nudge count (completions do not inflate it).

_assess_habit_stability's first OR'd criterion calls evaluate_frequency_step_down, so stacking stability transitively inherits this rate improvement for routine-cascade-completed habits. No local change at that call site; an integration test verifies the transitive effect.

Changes

  • app/services/graduation.py — evaluate_frequency_step_down now queries habit_completions over the scheduled_at span of the LIMIT-14 window. The already_done_count derivation iterates notifications: "Already done" responses count as before; expired notifications with response IS NULL count if a same-date completion exists; all other explicit responses are untouched. FrequencyStepResult shape unchanged.
  • tests/test_graduation.py — new TestEvaluateFrequencyStepDownHybridCredit class with 7 cases covering: routine-cascade credit, no-credit-without-row, mixed explicit+completion 100%, same-day de-dup, explicit-negative override guard, denominator-invariant guard, and the transitive stacking-stability integration test.

How to Verify

  1. git checkout feature/2G-Bug-03-step-down-hybrid-credit
  2. ruff check . — passes clean
  3. pytest -v tests/test_graduation.py — all 167 graduation tests pass, including the 7 new hybrid-credit tests
  4. pytest -v — full suite: 1299 passed, 4 skipped

Deviations

None. Implementation matches the GitHub issue acceptance criteria and [2G-03] v2 Amendment 15.

Per Wave 2 brief guidance (D7, Wave 3 #186): the hybrid-credit logic here is deliberately inline and near-identical to the #184 implementation on evaluate_graduation. The duplication is expected and retained — the shared read_completion_history helper refactor lands in Wave 3 as #186.

Test Results

```
$ ruff check .
All checks passed!

$ pytest -v
====================== 1299 passed, 4 skipped in 22.91s =======================
```

New tests in TestEvaluateFrequencyStepDownHybridCredit:

  • test_routine_cascade_completion_credited_toward_step_down
  • test_no_credit_when_no_completion_row_step_down
  • test_mixed_response_and_completion_step_down
  • test_same_day_nudge_and_completion_not_double_counted
  • test_explicit_negative_response_not_overridden_by_completion_step_down
  • test_extra_completions_outside_nudge_range_do_not_inflate_denominator
  • test_stacking_stability_inherits_step_down_rate_change

All existing step-down and stacking tests continue to pass (AC #8).

Acceptance Checklist

Closes #185

…ency_step_down (#185)

Mirrors D5 from #184 on the step-down surface. For each expired nudge
with no response in the LIMIT-14 window, credit the rate if a
HabitCompletion row exists for the same date. Completion range is
bounded by the scheduled_at span of the fetched notifications, so it
adapts naturally to the habit's current frequency.

Explicit responses still win: 'Already done' counts on its own, and
explicit non-'Already done' responses ('Skip today' etc.) are never
overridden. FrequencyStepResult shape unchanged; denominator stays at
nudge count.

Transitively improves _assess_habit_stability's first criterion for
routine-cascade-completed habits — verified via integration test.

Duplication with evaluate_graduation (#184) is expected and retained;
the shared read_completion_history helper refactor is Wave 3 #186.
@WilliM233
WilliM233 merged commit bd3ed64 into develop Apr 19, 2026
2 checks passed
@WilliM233
WilliM233 deleted the feature/2G-Bug-03-step-down-hybrid-credit branch April 19, 2026 17:12
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.

[2G-Bug-03]: Credit habit_completions rows toward evaluate_frequency_step_down "Already done" rate on expired nudges

1 participant