Skip to content

CAP-MBR-STR-015Determine a term’s start and end dates

Markdown

CAP-MBR-STR-015

Determine a term’s start and end dates

Markdown
  • DocumentDraft
  • Product intentProposed
  • ImplementationObserved

KindCapabilityProduct areaMembershipModuleOrganizational Structure, Membership Configuration and TermsRevision1

Read atservices-membership 5994c270react-console baf6dae3

The Formal Contract

The narrative above explains; the sections below are authoritative — the rules, scenarios and tests this document binds.

Outcomes
Date calculations produce repeatable periods from explicit inputs.Invalid date-rule combinations are rejected with actionable problems.
Surfaces
apijob
Intent sources
services-membership:README.md#2026-10-18 — Final acceptance, Decisions (Andrew: a calendar period ending in February ends on the month's real last day)services-membership:README.md#The term engine (D-45, decided 2026-10-17: the engine imports nothing of Membership and is extracted later)

Controls

21 controls apply, from the corpus control catalogue.

TC-AZ-001 TC-AZ-002 TC-AZ-003 TC-AZ-004 TC-AZ-005 TC-AZ-006 TC-CHG-001 TC-CHG-002 TC-CHG-003 TC-CHG-004 TC-IAM-001 TC-IAM-002 TC-IAM-004 TC-IAM-005 TC-LOG-004 TC-PI-001 TC-SEC-001 TC-SEC-004 TC-TEN-001 TC-TEN-002 TC-TEN-003

Permissions

KeyChecked atScope
membership.configuration.manageapitenant
membership.application.accessapitenant

Business Rules

13 rules, 12 to be proved before approval. A rule's product intent is derived from its origin: 1 confirmed by a product decision, the rest inheriting this capability's proposed.

IdRuleKindEnforced atProvenance
01must prove
Proposed
Term rules use anniversary, calendar or never-ending bases, require valid length and units where applicable and report incompatible or missing settings together.
no proof recorded yet
validationservice, databasetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: what 2.0 decides › reports every problem of a rule at once
02must prove
Proposed
Partial package changes validate the combined stored and submitted term rule rather than only submitted settings.
no proof recorded yet
validationserviceobserved
03must prove
Proposed
A continuing term begins the day after the existing term’s end regardless of purchase date unless continuation is disabled.
no proof recorded yet
calculationservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: the rules of 1.0 beyond its spec cases › extends an early renewal from the current end, whatever the basis
04must prove
Proposed
A terminated term continues through its end plus the reinstatement allowance, treating absence as zero days; purchases after that boundary restart.
no proof recorded yet
calculationserviceobserved
05must prove
Proposed
Anniversary rules apply their month-end rounding and new-term sell-after threshold to year, quarter and month terms, while day and week terms retain the full count.
no proof recorded yet
calculationservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: 1.0 calculate-expiration-date.spec.js › 1.0 lines 284-292: anniversary in years, should calculate properly for a single year › ends a purchase of 2019-04-03 at the end of the anniversary month, 2020-04-30
06must prove
Proposed
Fixed windows match an adjusted anniversary start; an immediately following date moves back only when the window end is in that start’s year, excluding the January-after-December boundary.
no proof recorded yet
calculationserviceobserved
07must prove
Proposed
Calendar rules end before their start month, use the sell-for-next-year threshold and backdate a fresh term to the period start while retaining a continuation’s start.
no proof recorded yet
calculationservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: 1.0 calculate-expiration-date.spec.js › 1.0 lines 70-77: calendar, a standard year, should calculate properly for April › back-dates a purchase of 2019-04-03 to 2019-01-01 and ends it 2019-12-31
08must prove
Proposed
A calendar period finishing in February uses the actual last day, including leap day.
origin: services-membership:README.md#2026-10-18 — Final acceptance, Decisions (Andrew: a calendar period ending in February ends on the month's real last day)
no proof recorded yet
calculationservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: what 2.0 decides › ends a calendar period ending in February on its last day in a leap year: 2024-02-29 (decided by Andrew 2026-10-18: the real last day of the month; 1.0 added a year with Moment and ended it 2024-02-28)
09must prove
Proposed
A never-ending rule returns its starting point with no end date.
no proof recorded yet
calculationservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: 1.0 calculate-expiration-date.spec.js › 1.0 lines 248-255: no expiration, should calculate properly › starts on 2019-04-03 and never ends
10must prove
Proposed
Date computation uses supplied inputs without reading a clock or mutating those inputs.
no proof recorded yet
invariantservicetest
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: what 2.0 decides › gives the same result twice for the same frozen inputs (1.0 turned the fixed dates into Moments in place, so a second call read other inputs)
11must prove
Confirmed
The term engine imports no other Membership module so it can be extracted when another domain adopts it.
origin: services-membership:README.md#The term engine (D-45, decided 2026-10-17: the engine imports nothing of Membership and is extracted later)
no proof recorded yet
invariantservicetest
services-membership:modules/terms/tests/unit/isolation.spec.ts#term engine isolation › imports nothing of the repository outside modules/terms
12
Proposed
Overlapping fixed windows are accepted and resolved by the first matching window in stored order.
policyserviceobserved
13must prove
Proposed
Equal start and end month-days create a year-spanning fixed window, with the result after the window end and additional years for multi-year terms.
no proof recorded yet
policyserviceobserved

Scenarios

IdGivenWhenThenProvenance
01A current annual term ends on the last day of June.The member purchases its continuation during May.The new period starts on the first day of July.test
services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: the rules of 1.0 beyond its spec cases › extends an early renewal from the current end, whatever the basis

Edge Cases

IdGivenWhenThenProvenance
02Two fixed windows end October-to-March purchases in June and April-to-September purchases in December.A first-time member purchases on the first day of April.The boundary adjustment selects the earlier window and a June end.documented

The Markdown files under specs/ in rhythmsoftware/rhythm-v2-docs are the canonical source of truth. This reader is a generated, read-only projection of them.