"Which rule is this lesson an instance of?" now has three recorded answers:
a rule named (a confirmed link, #4630), no rule fits (new), or unjudged.
- Model + migration 0112: lesson_no_rule (lesson_id PK, CASCADE from the
note; why; judged_at). A table rather than a key in notes.data, because
that mirror is re-composed from the body on every edit and would erase it.
- Service (lesson_rules): set_no_rule rejects any confirmed link with the
reason; a confirmation (set_lesson_rules or judge_link) deletes the answer;
require_one_answer refuses both answers in one call before any write;
judgments_for_lessons + attach_lesson_rules add rule_judgment (and no_rule)
to every lesson payload; list_unjudged lists the open ones; rule_candidates
searches rules with the lesson's claim + trigger at the explicit-search bar,
None when the search could not run.
- MCP: create_lesson/update_lesson take no_rule; an unanswered create returns
rule_candidates, rule_judgment and a rule_hint; list_lessons(unjudged=true).
- REST: the same on POST/PATCH /api/lessons and GET ?unjudged=1; create
returns rule_candidates.
- Backup v19: a lesson_no_rule section, export (full and per-user) and import.
- Guidance: create_lesson docstring, writing-records.md in using-scribe (owner,
pinned in test_guidance_ownership), create_rule docstring on linking the
lessons a new rule governs. Plugin version minted.
- Tests: door units, integration for the three states, the rejection reason,
scoping, cascade; backup registries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>