Integration test — the full cycle: seeded, aged out, hit again, back in the axis + text search
The story-level test. Each half has its own unit coverage; this proves they compose into a loop, which is the only form in which this feature is worth anything.
Drive one global lesson through every state, against the real Postgres:
| step | assert |
|---|---|
| seed a global lesson, tagged on the axes | it is returned by an axis + text search |
advance lastOccurredAt past the retention window | it is gone from the same search |
| file a planning bug describing the same mistake in different words | it MATCHES that lesson, not a new row |
| after the match | it is back in the same search, unchanged in content |
Re-word the recurring mistake rather than replaying the lesson's own text. A test that files the lesson verbatim proves string equality, not semantic matching — and the real signal never arrives in the original words. The match must survive paraphrase, which is the property the whole loop rests on.
Drive time by the retention setting, not a hard-coded 90. The window is a runtime setting; a test that bakes the number in goes red the day somebody tunes it, for no reason connected to correctness.
Also assert the negative, because it is the one that would otherwise never be checked: an UNRELATED planning bug does not match and does not revive anything. A matcher with a threshold loose enough to hit everything would pass every positive assertion above.
Acceptance criteria
- The four-step cycle passes end to end against the real test database, on a global lesson.
- The recurrence is filed as a paraphrase, not the lesson's own wording, and still matches.
- An unrelated bug matches nothing and revives nothing, asserted in the same suite.
- Time is advanced relative to the configured retention window, never a literal 90.
- Content is unchanged by revival — a recurrence bumps the clock, it does not rewrite the lesson.
- The suite fails if the revival step is removed, demonstrated once in the PR body.
- Only the changed files are run locally; CI runs the suite.
Context refs
motir-aitests/lessonLifecycle.test.ts— the existing lifecycle suite and its clock helpers.motir-aisrc/services/lessonService.ts—retirementCutoff,lessonRetentionDays,recordRecurrence.- MOTIR-3323 — the global reinforcement this exercises.
- MOTIR-3324 — the bug-to-lesson match this drives.