The one dedicated test card this parent earns. Every code card above ships its own unit floor (code + tests are one deliverable), so this card asserts only what those suites mock away: the cross-boundary seam, driven through the real MCP transport, exactly as MOTIR-3481 does for search_lessons.
search_lessons returns a ranked lesson; its id — taken from the STRUCTURED payload, not parsed out of the prose — is passed straight to reinforce_lesson, and the lesson's lastOccurredAt advances. This is the loop the parent exists to close, asserted end to end in one test.lesson:reinforce is refused and motir-ai is never called. Assert the refusal AND the absence of the upstream call.occurrenceRef twice returns success reporting that it did not count, and the counters are unchanged. A caller must be able to tell recorded from already recorded without reading an exception.outcome-style discipline search_lessons established, where two empty answers that mean opposite things must stay distinguishable.Do not re-derive the ledger's idempotency, the retired-row behaviour, or the generator's marker logic. Those are asserted in the motir-ai cards that ship them, and re-covering them here buys a second place for them to drift. If a clause of this card can be discharged inside one repository, it belongs in that repository's card.
search_lessons → reinforce_lesson through the real MCP transport and asserts the clock advanced on the returned lesson.MOTIR-3481 — the precedent: an integration vitest through the real MCP transport for the sibling read.lib/mcp/tools/searchLessons.ts — the structured channel this test reads the id from.lib/test-lessons-mock.ts — the existing lesson test double to extend rather than replace.MOTIR-3547 (the parent) — the rule, stated once.