diff --git a/src/scribe/routes/lessons.py b/src/scribe/routes/lessons.py index f3f11b5f..5af0ad65 100644 --- a/src/scribe/routes/lessons.py +++ b/src/scribe/routes/lessons.py @@ -7,18 +7,16 @@ contract and the `data` mirror live in one place — the same division snippets use, and for the same reason: two doors that each compose a lesson would compose it two ways, and the document IS what ranks. -ACL (rule #78): reads and writes of a single lesson resolve through the +ACL: reads and writes of a single lesson resolve through the share-aware `get_lesson` / `can_write_note`, and writes are performed as the OWNER so a shared editor isn't rejected by the owner-scoped service — mirroring routes/snippets.py and routes/notes.py. WHY THE TRIGGER IS A NAMED FIELD HERE TOO. The web editor could have posted a -body and let the service parse it. It doesn't, because the evidence behind -this kind (step 1) is that a trigger gets filled when a door ASKS for it by -name — the snippet corpus is at 100% on its trigger with no guard anywhere, -because a service composes the title from a parameter. A form that offered one -markdown box would be the option milestone 385 rejected, wearing a different -hat. +body and let the service parse it. It doesn't, because a trigger gets filled +when a door ASKS for it by name — a field the service composes from a named +parameter is filled every time, with no guard anywhere, and a single markdown +box is not. """ import logging @@ -63,7 +61,7 @@ async def list_lessons_route(): project_id = None limit, offset = parse_pagination(default_limit=24, max_limit=100) - # The lessons nobody has answered "which rule?" for (#4631). A listing, + # The lessons nobody has answered "which rule?" for. A listing, # so it does not combine with a search — the MCP door says the same. if request.args.get("unjudged") in ("1", "true"): if q: @@ -87,7 +85,6 @@ async def list_lessons_route(): # construction. Every row gets the key, zero-filled, so the UI renders # "never surfaced" rather than treating a missing field as a state. # - # Lessons were the one kind collecting this and showing it nowhere (#4196). # The counts matter more here than on a snippet: the promotion question a # lesson eventually raises — does this bind? — is answered by repeatedly # surfaced AND repeatedly opened, and the far commoner reading of the same @@ -122,8 +119,7 @@ async def lessons_taught_by_route(record_id: int): @lessons_bp.route("/rule-candidates", methods=["GET"]) @login_required async def lesson_rule_candidates_route(): - """The rules a lesson being written resembles, BEFORE it is saved - (milestone 440, #4658). + """The rules a lesson being written resembles, BEFORE it is saved. The create door offers the same list in its reply when neither answer was given. A form needs it earlier, so the writer can name the rule in the @@ -178,7 +174,7 @@ async def create_lesson_route(): project_id = data.get("project_id") or None learned_from = data.get("learned_from") or [] - # The rules this lesson is an instance of (milestone 440). Validated before + # The rules this lesson is an instance of. Validated before # anything is written, as the MCP door does, so a bad id saves nothing. no_rule = (data.get("no_rule") or "").strip() try: @@ -264,7 +260,7 @@ async def get_lesson_route(lesson_id: int): await lesson_rules_svc.attach_lesson_rules(uid, [out]) # Opening the detail view IS a pull — the operator chose to look. Tagged # apart from the MCP sources so "an agent was handed it" and "a human read - # it" stay distinguishable; they mean different things for pruning (#2085). + # it" stay distinguishable; they mean different things for pruning. record_pulled(user_id=uid, note_id=lesson_id, source="rest_lesson") return jsonify(out) @@ -322,7 +318,7 @@ async def update_lesson_route(lesson_id: int): if no_rule: await lesson_rules_svc.set_no_rule(uid, lesson_id, no_rule) if data.get("system_ids") is not None: - # The CALLER, not owner_uid (#4249). `set_record_systems` runs its own + # The CALLER, not owner_uid. `set_record_systems` runs its own # `can_write_note` and links only Systems the acting user can read; # handing it the owner makes that check trivially pass and filters by # the owner's visibility instead. The caller's write permission is @@ -331,7 +327,7 @@ async def update_lesson_route(lesson_id: int): await systems_svc.set_record_systems(uid, lesson_id, data["system_ids"]) out = lessons_svc.lesson_to_dict(updated) if no_rule: - # The same nudge the MCP door gives (#4634): this answer may complete + # The same nudge the MCP door gives: this answer may complete # a group of no-rule lessons in one situation. group = await lesson_rules_svc.convergence_for(uid, lesson_id) if group: