Integration test — a retired lesson stays retired through a recurrence, and only the retire permission can retire
The story-level seam test, and the assertion the whole story exists for.
The collision, end to end:
- Retire a tenant lesson.
- Confirm it is no longer returned by the injection read.
- Record a recurrence of exactly that lesson.
- Confirm it is still retired and still not injected.
Then the mirror, so the fix is not a blunt one:
- Take a different lesson that merely aged out — never retired,
lastOccurredAtpast the cutoff. - Record a recurrence; confirm it is revived and injected again.
Steps 4 and 6 must both hold. A change that stops recurrences touching the enabled state at all passes 4 and fails 6, and reads as a fix.
Also: un-retiring restores injection; a retired lesson is still returned by the inspection read with its actor and timestamp; retiring a global lesson is refused.
Drive time from the configured retention window, never a literal 90 — the window is a runtime setting and a hard-coded test goes red the day somebody tunes it.
⚠️ Amendment (2026-08-23) — a SEVENTH and EIGHTH step: the exemption, and that nothing fabricates an occurrence
Authored 2026-08-21; the parent story MOTIR-3330's 2026-08-23 amendment adds two of its own acceptance criteria that this seam test is the only place to assert:
- An aged-out lesson brought back STAYS back.
applyon a row whose clock is past the cutoff exempts it; advance the clock past the window again and confirm it is still injected. Asserted with the clock moved, never waited out. - Nothing writes
lastOccurredAtorrecurrenceCountto express a human decision. Read both before and after aretireand anapplyand assert they are unchanged — the occurrence history records only occurrences.
Step 7 is the mirror of step 4 the same way step 6 is: a fix that made exempt swallow the staleness predicate for every row would pass 7 and fail 6.
Acceptance criteria
- All six numbered steps pass against the real test database.
- Steps 4 and 6 are asserted separately and both hold.
- Un-retire restores injection.
- The inspection read still returns the retired lesson with actor and timestamp.
- A global retire attempt is refused with its typed error.
- Time is advanced relative to the configured window.
- Only the changed files are run locally; CI runs the suite.
Context refs
motir-aitests/lessonLifecycle.test.ts— the lifecycle suite and its clock helpers.motir-aisrc/repositories/lessonRepository.ts—recordRecurrence, the injection clause, the retirement state.- MOTIR-3343 — the precedence fix this proves.
- MOTIR-3344 — the endpoint it drives.