"""The document a lesson is embedded as (milestone 385 step 3). WHY THIS IS THE STEP THAT DECIDES THE MILESTONE Everything before this is storage. A lesson stored with a trigger but embedded as ordinary prose is a note wearing a label: it would look right in every listing and simply never be retrieved at the moment it applies, and nothing anywhere would report that. WHY THERE IS NO `lesson_document()` BESIDE `rule_document()` The step anticipated one. There isn't, and the difference is where the sharp shape LIVES rather than whether it exists. A rule keeps its trigger in a column and its title is a plain name, so the `{title} — {trigger}` document has to be synthesised at embed time and exists nowhere else — that is what `rule_document` is for. A snippet, which note #2485 measured as the only sharp record in the corpus (a 0.153 top-to-second gap against 0.010–0.023 for everything else), gets there the other way: its STORED title is already the join and its stored body already opens with the trigger, so the ordinary `title\\nbody` join is the sharp document. A lesson follows the snippet, which is what step 1 decided and step 2 built. The consequence worth stating: `chunk_document` is untouched, so `CHUNKER_VERSION` does not move and nothing re-embeds. The step's "Re-embed" section describes a change this design does not make. These guards therefore assert the composed record, then assert that the generic chunker turns it into the intended document — the two halves of the same claim. No similarity number is asserted anywhere: a threshold pins the embedder's behaviour rather than this code's, and breaks on a model change that is not a regression. """ from __future__ import annotations from scribe.services import lessons as lessons_svc from scribe.services.embeddings import chunk_document, embedding_text TRIGGER = "a test fails on code you believe is correct" SUBJECT = "Suspect the guard before the code" INSIGHT = "Check whether the assertion still describes the property it was written for." def test_the_trigger_appears_twice_in_the_document(): """THE guard. Purpose stated twice in a short document is the entire measured cause of a snippet's sharpness, and it is the one property that distinguishes a lesson's vector from a plain note's.""" title, body = lessons_svc.lesson_document(SUBJECT, TRIGGER, INSIGHT) document = embedding_text(title, body) assert document.count(TRIGGER) == 2 # Once in each half, not twice in one of them. assert TRIGGER in title assert TRIGGER in body def test_the_document_leads_with_when_it_applies(): """The title is `{what} — {when}` and the body's FIRST line restates it, so the opening of the document is about the situation rather than the topic. A lesson buried behind a paragraph of narrative would rank on the narrative.""" title, body = lessons_svc.lesson_document(SUBJECT, TRIGGER, INSIGHT) assert title == f"{SUBJECT} — {TRIGGER}" assert body.splitlines()[0] == f"**When to apply:** {TRIGGER}" def test_a_short_lesson_is_exactly_one_chunk(): """`chunk_document`'s first contract line: a record inside the window yields one chunk identical to the historical `title\\nbody`. A lesson that split into several would spread the trigger's weight across vectors that each carry less of it.""" title, body = lessons_svc.lesson_document(SUBJECT, TRIGGER, INSIGHT) chunks = chunk_document(title, body) assert len(chunks) == 1 assert chunks[0].count(TRIGGER) == 2 def test_a_long_lesson_keeps_the_trigger_on_every_chunk(): """The narrative question, answered by the chunker rather than by holding the story out of the record. `rule_document` excludes a rule's `why` because long dated narrative made sixteen dev-logs land on the centroid of "development". That finding predates chunking (#280): a body over budget is now split, and EVERY chunk is prefixed with the title — which for a lesson carries the trigger. So the story occupies its own vectors instead of averaging itself into the trigger's, and each of those vectors is still anchored to when the lesson applies. This is why the insight stays in the body where a reader can see it. Holding it out would cost the reader the only part that explains the lesson, to buy a sharpness the chunker already provides. """ narrative = "\n\n".join( f"## Section {i}\n" + ("An unrelated sentence about deployment. " * 40) for i in range(6) ) title, body = lessons_svc.lesson_document(SUBJECT, TRIGGER, narrative) chunks = chunk_document(title, body) assert len(chunks) > 1, "the fixture must actually exceed the chunk budget" assert all(TRIGGER in chunk for chunk in chunks) def test_a_lesson_with_no_trigger_still_embeds(): """Degrades to title + insight, the way a rule with no trigger does — less sharply, and still findable. That is an argument for prompting hard for a trigger at write time, not for padding the document with whatever text is to hand.""" title, body = lessons_svc.lesson_document(SUBJECT, "", INSIGHT) assert title == SUBJECT assert body == INSIGHT assert chunk_document(title, body) == [f"{SUBJECT}\n{INSIGHT}"] def test_the_composed_body_is_the_one_the_reader_is_parsed_back_from(): """`compose_body` writes the trigger line and `lesson_trigger` reads it. A lesson whose mirror in `data` is missing still answers correctly, so the two must agree on the exact markdown — which is why neither is written by hand at a call site.""" from types import SimpleNamespace _, body = lessons_svc.lesson_document(SUBJECT, TRIGGER, INSIGHT) no_mirror = SimpleNamespace(data=None, body=body) assert lessons_svc.lesson_trigger(no_mirror) == TRIGGER def test_the_title_and_body_are_composed_by_one_call(): """`lesson_document` returns both halves so they cannot be built apart. A title carrying the trigger over a body that does not would embed as an ordinary note, and every listing would still look correct.""" assert lessons_svc.lesson_document(SUBJECT, TRIGGER, INSIGHT) == ( lessons_svc.compose_title(SUBJECT, TRIGGER), lessons_svc.compose_body(INSIGHT, TRIGGER), )