feat(lessons): judged when written — a new lesson is offered its rules, and "no rule fits" is an answer (milestone 440 step 2, #4631)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 53s
CI & Build / Python tests (push) Failing after 1m15s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 53s
CI & Build / Python tests (push) Failing after 1m15s
CI & Build / Build & push image (push) Skipped
"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>
This commit is contained in:
@@ -27,7 +27,7 @@ def test_backup_version_is_current():
|
||||
|
||||
(Named for the number it asserted until v10, which is exactly the drift a
|
||||
name-carrying-a-value invites; it now says what it checks.)"""
|
||||
assert backup.BACKUP_VERSION == 18
|
||||
assert backup.BACKUP_VERSION == 19
|
||||
|
||||
|
||||
def _exportable_note(**over):
|
||||
@@ -131,7 +131,7 @@ def test_a_repo_binding_carries_the_branch_its_ledger_follows():
|
||||
def _column_guard_targets():
|
||||
from scribe.models.canonical_system import CanonicalSystem
|
||||
from scribe.models.code_shape import CodeShape, CodeShapeEvent, CodeShapeUse
|
||||
from scribe.models.lesson_rule_link import LessonRuleLink
|
||||
from scribe.models.lesson_rule_link import LessonNoRule, LessonRuleLink
|
||||
from scribe.models.design_system import DesignSystem, DesignToken
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.models.note import Note
|
||||
@@ -169,6 +169,7 @@ def _column_guard_targets():
|
||||
"note_supersessions": (NoteSupersession, backup._note_supersession_rows),
|
||||
"rule_relations": (RuleRelation, backup._rule_relation_rows),
|
||||
"lesson_rule_links": (LessonRuleLink, backup._lesson_rule_link_rows),
|
||||
"lesson_no_rule": (LessonNoRule, backup._lesson_no_rule_rows),
|
||||
"note_usage_events": (NoteUsageEvent, backup._usage_event_rows),
|
||||
"rule_usage_events": (RuleUsageEvent, backup._rule_usage_event_rows),
|
||||
"retrieval_tuning_events": (
|
||||
@@ -286,6 +287,7 @@ def _import_guard_targets():
|
||||
"note_supersessions": backup._build_note_supersession,
|
||||
"rule_relations": backup._build_rule_relation,
|
||||
"lesson_rule_links": backup._build_lesson_rule_link,
|
||||
"lesson_no_rule": backup._build_lesson_no_rule,
|
||||
"note_usage_events": backup._build_usage_event,
|
||||
"rule_usage_events": backup._build_rule_usage_event,
|
||||
"retrieval_tuning_events": backup._build_retrieval_tuning_event,
|
||||
@@ -539,7 +541,9 @@ async def test_export_full_backup_contains_every_declared_section():
|
||||
# v16: the reasons beside the settings they explain.
|
||||
"retrieval_tuning_events",
|
||||
# v18: which rule each lesson is an instance of.
|
||||
"lesson_rule_links"):
|
||||
"lesson_rule_links",
|
||||
# v19: the lessons judged to fall under no rule.
|
||||
"lesson_no_rule"):
|
||||
assert key in out, f"missing export section: {key}"
|
||||
assert out[key] == []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user