Skip to content

Add Interval cycle kind (I notation)#71

Open
rymiwe wants to merge 2 commits intomainfrom
feat/interval-cycle-kind
Open

Add Interval cycle kind (I notation)#71
rymiwe wants to merge 2 commits intomainfrom
feat/interval-cycle-kind

Conversation

@rymiwe
Copy link
Contributor

@rymiwe rymiwe commented Mar 17, 2026

Summary

New cycle type for repeating windows anchored to a from_date, split from #69 per review feedback.

V1I24MF2026-03-31 = complete 1 every 24 months from March 31, 2026. After completion, the consuming app re-anchors from the completion date via reactivated_notation(completion_date).

Review feedback addressed (from #69)

  1. Split PR — EndOf fix is in Fix EndOf cycle final_date off-by-one #70, this PR is Interval-only
  2. Naming collision — Renamed from Recurring to Interval to avoid collision with Cycle#recurring? predicate (which means "does this cycle type repeat?" — Lookback, Calendar, EndOf all return true)
  3. Notation letter — Changed from R to I to match the convention (every kind uses its first letter: Lookback, Calendar, Within, EndOf, Interval)
  4. Dormant capability — Declared via def self.dormant_capable? = true on the class (uses the pattern from Move dormant_capable? from Parser to cycle classes #73 instead of editing Parser's hardcoded list)
  5. Differentiated from Within#to_s uses "every...from" format vs Within's "within...range"; #description explains re-anchoring; #examples show the distinction
  6. Justification for separate classI notation makes re-anchoring intent explicit in the notation string, so consuming apps don't need external metadata to know which W cycles should re-anchor

Bonus: reactivated_notation alias

reactivated_notation(date) aliases activated_notation(date) for self-documenting call sites when re-anchoring a satisfied cycle.

Key differences from Within

Aspect Within (W) Interval (I)
recurring? false true
dormant_capable? true true
#to_s (active) "1x within start - end" "1x every 24 months from start"
#to_s (dormant) "1x within 24 months" "1x every 24 months"
extend_period Supported Not supported (re-anchors instead)
Lifecycle One-shot window Re-anchors from completion date

Test plan

  • All 208 gem specs pass (179 existing + 25 Interval + 4 dormant/parity coverage)
  • StandardRB clean

Addresses QUAL-6317
Supersedes #69 (Interval portion)
Built on #73 (merged) and #70 (merged)

@rymiwe rymiwe force-pushed the feat/interval-cycle-kind branch 2 times, most recently from a96a4be to c9c5e5c Compare March 17, 2026 17:05
@rymiwe rymiwe changed the title Add Interval cycle kind (R notation) Add Interval cycle kind (I notation) Mar 17, 2026
rymiwe added a commit that referenced this pull request Mar 18, 2026
Alternative to #71 (Interval) — same behavior, different class structure.
RepeatingWithin subclasses Within to make the relationship explicit.

V1I24MF2026-03-31 = complete 1 every 24 months from March 31, 2026.
After satisfaction, consuming app calls reactivated_notation(date) to
start the next window.

Overrides from Within:
- recurring? → true (Within is false)
- to_s → "every...from" format (Within uses "within...range")
- extend_period → no-op (re-anchors instead of extending)
- last_completed → returns from_date (current window anchor)
- expiration_of → returns final_date
- satisfied_by? → checks anchor <= final_date
- start_date/final_date → nil-safe for dormant state

Also adds reactivated_notation(date) alias for activated_notation to
make the re-anchoring call site self-documenting.
@rymiwe rymiwe marked this pull request as ready for review March 20, 2026 06:07
@rymiwe rymiwe requested review from a team March 20, 2026 06:20
@rymiwe rymiwe force-pushed the feat/interval-cycle-kind branch from 52f0e05 to d0f186b Compare March 20, 2026 06:35
rymiwe added 2 commits March 20, 2026 11:02
New cycle type for repeating windows anchored to a from_date.
V1I24MF2026-03-31 = complete 1 every 24 months from March 31, 2026.
After completion, consuming app re-anchors from the completion date.

Named "Interval" (not "Recurring") to avoid collision with the existing
Cycle#recurring? predicate which indicates whether a cycle type repeats.

Key differences from Within:
- recurring? returns true (Within returns false)
- #to_s uses "every...from" format (Within uses "within...range")
- No extend_period (window re-anchors, not extends)

Dormant behavior handled by Dormant wrapper via Parser.dormant_capable_kinds,
consistent with Within pattern (no redundant guards in class methods).
- reactivated_notation(date) aliases activated_notation for
  self-documenting call sites when re-anchoring a satisfied Interval
- Specs verify Interval computes final_date/start_date identically
  to Within, documenting the conceptual relationship
@rymiwe rymiwe force-pushed the feat/interval-cycle-kind branch from d0f186b to 19baab8 Compare March 20, 2026 18:02
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