Search the library BEFORE filing — the planning bug carries whether a lesson already exists, so a hit needs no new lesson
The search comes before the filing, and its result belongs on the bug.
filePlanningBug files and returns — no embedding, no lookup, no recordRecurrence. So the one moment we know a mistake just happened, we never ask whether we already knew about it.
The order matters, because the answer changes what is owed:
- Search the library first — the card's axes plus the planner's own description of what went wrong, through the same matcher and threshold capture uses. One matcher, not two.
- HIT — the same mistake has recurred.
recordRecurrence: bumplastOccurredAt, re-assertenabled, so the lesson returns to the axis + text search even if it had aged out. No new lesson is needed, and therefore no new row in the authored source and no new migration. The corpus already knew; it just needs to know it is still happening. - MISS — genuinely new. A lesson is owed: a row in
lessons.base.tsand the migration the generator emits from it. - The bug carries the answer either way — the matched lesson and how many times it has now recurred, or an explicit "no existing lesson matched, a new one is owed."
That last line is the deliverable, not decoration. A reader of the planner-bug home should be able to tell a third recurrence of one known mistake from a genuinely new one without re-deriving it, because the two need opposite work: one needs nothing written, the other needs a lesson authored and shipped.
Matching stays an enhancement: if the library cannot be reached, the bug is still filed, with the match recorded as unattempted rather than as a miss. A miss and a failed lookup must not look the same — one says write a lesson, the other says the check did not run.
Acceptance criteria
- The library is searched before the bug is filed, using the same embedding, axis narrowing and distance threshold as capture.
- A hit calls
recordRecurrenceand the bug names the matched lesson and its recurrence count. - A hit records that no new lesson is owed, so nobody authors a duplicate row or a redundant migration.
- A miss records that a new lesson IS owed, and names what has to happen for it to ship.
- A failed or unavailable lookup is recorded as unattempted, distinctly from a miss, and the bug still files.
- A hit on an already-expired lesson brings it back into
listForInjectionresults, asserted end to end. - Only fires on the agent-fired path — a user-initiated re-plan reaches none of this.
Context refs
motir-aisrc/services/lessonService.ts—filePlanningBug,renderPlanningBugBody,captureMistake's matcher,CAPTURE_DEDUP_DISTANCE.motir-aisrc/repositories/lessonRepository.ts—findNearestTenant,recordRecurrence.- MOTIR-3324 — the trigger gate that decides whether this runs at all.
- MOTIR-3323 — global reinforcement, without which a base lesson can never be the hit.
- MOTIR-3307 — the generator whose migration a MISS ultimately owes.