The door a hand-run sweep actually calls. search_lessons already hands the caller everything it needs to name a lesson; this is the write that closes the loop.
search_lessons took (MOTIR-3480)motirAiClient.reinforceLesson — the boundary call onto POST /v1/lessons/:id/reinforce, mirroring searchLessons's request/response typing.projectLessonsService.reinforceLesson — the permission assert happens HERE, BEFORE the upstream call, exactly as searchLessons asserts lesson:view before spending the caller's budget.reinforce_lesson MCP tool — args projectKey, lessonId, occurrenceRef, optional note. Registered in lib/mcp/registry.ts.lib/mcp/payloads/exemptions.ts, beside the search_lessons and add_lesson entries and for the same stated reason: no /api/v1 operation returns a lesson.lesson:managelesson:manage is the key add_lesson takes and the key retiring a lesson takes — a policy decision that removes a row from every future injection. Reinforcement is its opposite: additive, idempotent, and the thing we want a routine run to do freely. Granting lesson:manage to reach it would hand every dispatched agent the ability to retire. Add lesson:reinforce and grant that.
⚠️ Widening CLI_TOKEN_GRANT is not a one-line edit — it trips FOUR guards. lib/mcp/toolPermissions.ts is only the first; find and satisfy the rest in the same PR rather than discovering them in CI.
reinforce_lesson is registered and callable, taking projectKey, lessonId and occurrenceRef; the lesson id is the one search_lessons returns, and this card changes nothing about search_lessons to obtain it.projectLessonsService.reinforceLesson asserts lesson:reinforce before the motir-ai call; a caller without it is refused without the upstream ever being reached. Asserted.lesson:reinforce exists as its own key and is in CLI_TOKEN_GRANT; lesson:manage is NOT added to that grant, and a test pins that a CLI-minted token can reinforce and cannot retire.CLI_TOKEN_GRANT is updated in this same PR and the PR body lists them.occurrenceRef succeeds and reports that it did not count — the tool surfaces the ledger's answer rather than swallowing it.exemptions.ts entry documents the returned payload, in the register its two siblings use.lib/mcp/tools/searchLessons.ts — the adapter shape: getByKey, the gate placement, toolOk(prose, exempt(...)).lib/mcp/tools/addLesson.ts — the WRITE-side tool shape and its lesson:manage assert.lib/services/projectLessonsService.ts — searchLessons (the assert-before-boundary pattern), toRankedLessonDTO (:162).lib/ai/motirAiClient.ts — searchLessons / RawRankedLesson (:1153).lib/mcp/registry.ts, lib/mcp/toolPermissions.ts (CLI_TOKEN_GRANT), lib/mcp/payloads/exemptions.ts (:70).MOTIR-3480 — the six-file precedent this follows. MOTIR-3547 (the parent) — the rule, stated once.