diff --git a/tests/test_contract_around_the_change.py b/tests/test_contract_around_the_change.py index 919b3b1d..eb82b4a4 100644 --- a/tests/test_contract_around_the_change.py +++ b/tests/test_contract_around_the_change.py @@ -1,16 +1,14 @@ -"""You altered the shape of something — here is everything that reads it (#4215). +"""You altered the shape of something — here is everything that reads it. WHY THIS EXISTS -Milestone 419's most repeated miss, five of seven: acting on the thing in hand -without reading the contract around it. Lesson #4207 says it in words — -"widening a tuple is an interface change to every unpack site, and the -compiler will not tell you" — and was written by its author HOURS before a -structurally identical mistake, having been surfaced twice in the turns -between. Text delivered at the moment of acting is too weak a carrier for a -reflex that has to change what the act IS. This looks it up instead. +The commonest miss is acting on the thing in hand without reading the contract +around it: "widening a tuple is an interface change to every unpack site, and +the compiler will not tell you". Knowing that in words does not stop it — text +delivered at the moment of acting is too weak a carrier for a reflex that has +to change what the act IS. This looks it up instead. -Rule 33 one scope down: its checks are between layers ("every parameter the +Layer parity one scope down: its checks are between layers ("every parameter the caller sends is read by the handler under the same name"), and the same question exists between a definition and its callers. @@ -21,18 +19,17 @@ nothing alike in source: arg its parameter names arity and order key quoted keys of dict literals the shape of what it RETURNS -The third is here because of the miss that produced this step, and it is the -one a signature-watcher would have missed: `get_writepath_config` gained one -dict key, three arms read that dict inside a fail-open `except`, every one -silently became a no-op, and ten tests went red at once with nothing pointing -at the cause. No signature changed. +The third is the one a signature-watcher misses: a function gains one dict +key, three callers read that dict inside a fail-open `except`, every one +silently becomes a no-op, and tests go red at once with nothing pointing at +the cause. No signature changed. WHAT IS PINNED: which edits speak and which stay silent, and that both gates are load-bearing. NOT pinned: the wording, which is prose. A FIXTURE REPO, NEVER THIS ONE. Asserting against Scribe's own files would make the test a description of today's tree, failing the next time someone -renames something (rule 115's reasoning, one floor down). +renames something. """ import subprocess from pathlib import Path diff --git a/tests/test_family_models.py b/tests/test_family_models.py index c45ec23d..d62d6403 100644 --- a/tests/test_family_models.py +++ b/tests/test_family_models.py @@ -1,11 +1,10 @@ -"""Family canon's schema — the parts that need no database (milestone 463 step 1). +"""Family canon's schema — the parts that need no database. The constraints themselves, against real Postgres, are in tests/test_integration_family_canon.py. These pin that the migration and the -model agree on every whitelist (rule 36: a value one side accepts and the +model agree on every whitelist (a value one side accepts and the other refuses only fails at INSERT time, which CI's unit lane never reaches), -and that the seed ships nothing an install other than this one would find -foreign (rule 115). +and that the seed ships nothing a stranger's install would find foreign. """ from __future__ import annotations @@ -62,14 +61,14 @@ def test_the_column_defaults_match_the_first_state_of_each_lifecycle(): def test_a_canon_ideas_scope_lives_on_the_idea_and_nowhere_else(): """One scope source. A rule topic is linked FROM the idea and carries no platforms of its own, so the two cannot disagree about who a standard - reaches (#4221's two-sources-of-truth shape).""" + reaches — two sources of truth.""" from scribe.models.rulebook import RulebookTopic assert "topic_id" in family.FamilyIdea.__table__.c assert not {c.name for c in RulebookTopic.__table__.c} & {"platform_id", "platform_ids"} -# --- a decision read as precedent (#5131) ------------------------------------------ +# --- a decision read as precedent ------------------------------------------------ def _assessment(): reason = "the APK is built in CI and signed with the one release key" diff --git a/tests/test_frontend_request_deadlines.py b/tests/test_frontend_request_deadlines.py index 65abd0d4..90ec31ab 100644 --- a/tests/test_frontend_request_deadlines.py +++ b/tests/test_frontend_request_deadlines.py @@ -1,4 +1,4 @@ -"""Every request the web UI makes has a deadline (rule 156). +"""Every request the web UI makes has a deadline. A source-inspection guard in the unit lane — there is no frontend test runner, and this is a property of the source rather than of a rendered result, so @@ -7,7 +7,7 @@ reading the source is the honest way to check it. WHY. `fetch`'s default is to wait as long as the browser will. That is not a long timeout, it is the absence of one, and there is no state a surface can render for "pending forever" that is not a lie — the spinner that never -resolves is indistinguishable from work still in progress. Rule 156 names +resolves is indistinguishable from work still in progress. The practice names `fetch` specifically: When a library's default is "wait indefinitely" — `fetch`, most HTTP @@ -67,17 +67,15 @@ def test_every_fetch_passes_a_signal(): if "signal:" not in call ] assert not naked, ( - "these fetch calls carry no AbortSignal, so they wait forever " - "(rule 156):\n " + "\n ".join(naked) + "these fetch calls carry no AbortSignal, so they wait forever:\n " + "\n ".join(naked) ) def test_the_client_applies_its_deadline_by_default(): """The specific regression that would silently undo this. - An earlier pass (#3329) made `timeoutMs` OPT-IN and used it at exactly one - call site, which left ~330 others waiting forever while the mechanism - looked present. Reverting to that shape would not fail the guard above — + An OPT-IN `timeoutMs` used at one call site leaves every other call + waiting forever while the mechanism looks present. That shape would not fail the guard above — every call would still reach `fetch` through `request()` — so the default is pinned here separately. @@ -93,7 +91,7 @@ def test_the_client_applies_its_deadline_by_default(): def test_a_timeout_arrives_as_the_error_shape_callers_already_handle(): - """Rule 156's second half: expiry surfaces as a NAMED failure. + """The deadline's second half: expiry surfaces as a NAMED failure. A raw `DOMException: TimeoutError` reaches `apiErrorMessage(e, fallback)` as an object with no `body`, so every catch site in the app would print its diff --git a/tests/test_integration_lesson_kind.py b/tests/test_integration_lesson_kind.py index 9d907cf1..d6525f83 100644 --- a/tests/test_integration_lesson_kind.py +++ b/tests/test_integration_lesson_kind.py @@ -1,17 +1,17 @@ -"""A lesson is a row the database actually accepts (milestone 385 step 2). +"""A lesson is a row the database actually accepts. WHY THIS IS THE REAL GUARD, AND WHY IT IS HERE -The step asked for "a guard that the CHECK actually accepts `lesson` and -rejects a typo", citing #3128 — a `spike` kind that could not be written on an -instance predating its migration. That failure mode belongs to `task_kind`, +The obvious ask is a guard that a CHECK accepts `lesson` and rejects a typo — +the failure where a new kind cannot be written on an instance predating its +migration. That failure mode belongs to `task_kind`, which IS gated (`notes_task_kind_check`, migrations 0056 and 0065). `note_type` is not gated at all: migration 0036 added it as plain Text with a server default and nothing has constrained it since. So there is no whitelist to expand and no rejection to assert. Writing the -guard as "the constraint admits lesson" would have pinned a constraint that -does not exist; writing it as "no constraint exists" would pin today's schema +guard as "the constraint admits lesson" would pin a constraint that does not +exist; writing it as "no constraint exists" would pin today's schema rather than the behaviour that matters, and would go red on a change that is perfectly fine. @@ -19,7 +19,7 @@ Asserting the WRITE covers both worlds. It passes today, it keeps passing if a CHECK is added that admits `lesson`, and it goes red the day one is added that does not — which is the only outcome anyone needs to be told about. -The second test is the cell #3163 asks to be left empty on purpose: a lesson +The second test is the cell left empty on purpose: a lesson must not be a task. `is_task` is a read-only property over `status`, so this cannot be asserted by setting a flag — it has to be observed on a real row. """ @@ -83,13 +83,13 @@ async def test_a_lesson_is_a_row_the_database_accepts(owner_id): # and the readable body — because the vector is built from the text and # the queries are built from the mirror. assert lessons_svc.lesson_trigger(stored) == TRIGGER - # The subject alone (milestone 427): the trigger is in `data` and the body. + # The subject alone: the trigger is in `data` and the body. assert stored.title == SUBJECT assert "**When to apply:**" in (stored.body or "") async def test_a_lesson_is_not_a_task(owner_id): - """#3163's cell left empty on purpose. + """The cell left empty on purpose. `is_task` is `status is not None` and is read-only, so this is only observable on a stored row. A lesson that arrived with a status would join diff --git a/tests/test_ledger_references_not_silence.py b/tests/test_ledger_references_not_silence.py index a190a195..8fcca1ce 100644 --- a/tests/test_ledger_references_not_silence.py +++ b/tests/test_ledger_references_not_silence.py @@ -1,22 +1,20 @@ -"""A repeat on a note arm is referenced, not withheld (#4101). +"""A repeat on a note arm is referenced, not withheld. WHY THIS EXISTS -#3750 settled the argument for rules: a record the session was told about an -hour ago is not a record in front of the reader now, so the second time it is +A record the session was told about an hour ago is not a record in front of the reader now, so the second time it is the best answer the session gets the line again with a tail saying so — never silence, which is indistinguishable from "nothing matched". -The note and snippet arms never got that fix. Worse, their ledger went into -`semantic_search_notes` as `exclude_ids`, so the repeat left the candidate set -entirely, which had three consequences: +The note and snippet arms hold the same line. A ledger passed into +`semantic_search_notes` as `exclude_ids` takes the repeat out of the candidate +set entirely, with three consequences: - - the session got silence the second time, on the arms that fire most; - - a compaction made it permanent, because the ledger outlived the context it - described (the same step fixes that one layer down); - - and `best_available` was measured against a candidate set the caller had - already edited, so the bar could be blamed for a record the caller withheld - (#3739, from the side its fix never reached). + - the session gets silence the second time, on the arms that fire most; + - a compaction makes it permanent, because the ledger outlives the context it + describes; + - and `best_available` is measured against a candidate set the caller has + already edited, so the bar can be blamed for a record the caller withheld. WHAT THIS PINS @@ -28,11 +26,11 @@ WHAT THIS PINS and a dev-log borrowing it would claim authority it does not have — so what is pinned is that the line appears and is distinguishable, not its prose. - 3. The telemetry splits the two (#3752 / #3668): the row's result set and the + 3. The telemetry splits the two: the row's result set and the surfaced table both take FRESH only, and the repeat is COUNTED in `suppressed` instead. This is what makes a zero-result call readable — `result_count == 0` with `suppressed_count > 0` says "everything that - matched, the session has already seen", which was unreportable here. + matched, the session has already seen". 4. The deliberate exception: the write-path SYNC class still shows once. Its claim is about an edit in progress, not about a record's continuing relevance, and repeating it would be nagging rather than reminding. @@ -141,7 +139,7 @@ async def test_a_call_where_everything_was_already_seen_is_readable(): @pytest.mark.asyncio async def test_a_repeat_is_not_recorded_as_a_fresh_surfacing(): - """#3668's identity: this table and the log row describe the same call.""" + """The two-table identity: this table and the log row describe the same call.""" surf = MagicMock() hits = [(0.80, fake_note(id=11, title="Pool sizing decision", user_id=1)), (0.78, fake_note(id=22, title="run_maintenance thresholds", user_id=1))] diff --git a/tests/test_milestone_summary_brief.py b/tests/test_milestone_summary_brief.py index fdf8bffc..5ae28ece 100644 --- a/tests/test_milestone_summary_brief.py +++ b/tests/test_milestone_summary_brief.py @@ -1,9 +1,9 @@ -"""The enter_project handshake stays a small primer (#4045). +"""The enter_project handshake stays a small primer. -enter_project once carried every milestone's full plan body, the whole project -record, full rule text and ~9k of design guidance. On a project with 39 -milestones that came to ~222k characters, past what an MCP client accepts as -a tool result, so the session handshake arrived as a file to page through. +Every milestone's full plan body, the whole project record, full rule text and +the design guidance add up, on a project with a few dozen milestones, to more +than an MCP client accepts as a tool result — and the session handshake +arrives as a file to page through. """ import contextlib import json @@ -66,7 +66,7 @@ def test_brief_rows_leave_out_the_plan_and_what_the_caller_already_knows(): "id": 1, "title": "M1", "description": "what M1 is for", "status": "active", "order_index": 1, "total": 4, "completed": 2, "pct": 50.0, "status_counts": {"todo": 2, "in_progress": 0, "done": 2, "cancelled": 0}, - # Trimmed of the plan body, but NOT of which step is next (#4154): the + # Trimmed of the plan body, but NOT of which step is next: the # listing is where "2 of 4 done" gets read, and that number is exactly # what invites a reader to name the open step from memory. "next_step": {"id": 901, "title": "M1 step 3", "status": "todo"}, @@ -187,7 +187,7 @@ async def test_open_tasks_name_their_milestone_even_when_it_is_not_listed(): @pytest.mark.asyncio async def test_omitted_key_is_absent_when_nothing_was_left_out(): - """Attached only when it applies (#2483).""" + """Attached only when it applies.""" out, _ = await _enter(*_enter_stubs(fake_project(id=5), _history(3), [])) assert len(out["milestone_summary"]) == 3 assert "milestone_summary_omitted" not in out @@ -217,8 +217,8 @@ async def test_list_milestones_lists_every_milestone_without_plans(): def test_a_planning_read_lists_rules_without_restating_them(): - """#4081: a project's listing is every global rule tagged to its areas, so - the full rule_brief of each put start_planning at 92k characters. Planning + """A project's listing is every global rule tagged to its areas, so the + full rule_brief of each would make a planning read enormous. Planning reads name the rules; get_rule reads one.""" from scribe.services.rulebooks import rules_payload @@ -241,7 +241,7 @@ def test_a_planning_read_lists_rules_without_restating_them(): assert len(json.dumps(out)) < 6_000, len(json.dumps(out)) -# ── Milestones with no steps are open work (milestone 415) ───────────────── +# ── Milestones with no steps are open work ───────────────────────────────── def _planless(mid: int, status: str = "active") -> dict: diff --git a/tests/test_reporting_back_skill.py b/tests/test_reporting_back_skill.py index aa1de52e..48915c4c 100644 --- a/tests/test_reporting_back_skill.py +++ b/tests/test_reporting_back_skill.py @@ -1,4 +1,4 @@ -"""The reporting-back skill keeps its shape (milestone 409 step 2). +"""The reporting-back skill keeps its shape. WHY THIS EXISTS @@ -14,8 +14,8 @@ Structure, never wording — the same reason test_create_tools_disambiguate gives: a test that punishes rewriting gets deleted. It pins that the completion report keeps its five sections, that placement is taken from the record rather than recalled, and that the shipped shapes stay domain-neutral. -Whether the guidance is any good is milestone 409's last step, read against -real replies, not something a test can see. +Whether the guidance is any good is read against real replies, not something a +test can see. """ import pathlib import re @@ -39,14 +39,13 @@ def test_the_completion_report_keeps_its_sections(): def test_a_request_for_approval_has_its_own_named_section(): - """The operator, 2026-09-15 (#4084): a session's go-ahead request sat inside - a completion list as "Blocked by the permission check", and read as a - fault rather than a question waiting on them. The heading is what they - scan for, so it is pinned by name.""" + """A go-ahead request inside a completion list, as "Blocked by the + permission check", reads as a fault rather than a question waiting on the + operator. The heading is what they scan for, so it is pinned by name.""" from scribe.services import reply_shapes assert "Approval requested" in _text() - # The kinds moved to the delivered shapes (milestone 500): the asks shape + # The kinds live in the delivered shapes: the asks shape # carries the Approval kind, and the core sends a held action there. assert "**Approval**" in reply_shapes.SHAPES["asks"].text assert "Approval requested" in reply_shapes.core().text @@ -70,7 +69,7 @@ def test_the_shipped_shapes_assume_no_particular_domain(): def test_the_skill_points_at_the_delivered_shapes_instead_of_restating_them(): - """Milestone 500 step 4: the shapes are server product, delivered at their + """The shapes are server product, delivered at their moments. The skill says where they come from and holds the reasoning.""" text = _text() assert "list_reply_shapes" in text and "Reply shape · Every reply" in text diff --git a/tests/test_routes_snippets.py b/tests/test_routes_snippets.py index 32496365..859eb5de 100644 --- a/tests/test_routes_snippets.py +++ b/tests/test_routes_snippets.py @@ -41,9 +41,8 @@ def test_service_functions_take_user_id(): def test_agent_and_web_surfaces_stay_at_parity(): """The MCP tools and the REST routes are two callers of one service; a - capability on one has to exist on the other (rule #33). This guard exists - because they drifted apart once: MCP had no delete or `locations`, and the - web side had no `system_ids` and no near-duplicate gate.""" + capability on one has to exist on the other. Two doors maintained + separately drift — a delete on one, a near-duplicate gate on the other.""" from scribe.mcp.tools import snippets as tools from scribe.routes import snippets as routes @@ -65,13 +64,13 @@ def test_agent_and_web_surfaces_stay_at_parity(): assert "system_ids" in inspect.getsource(route) assert "find_duplicate_note" in inspect.getsource(routes.create_snippet_route) - # The drift check (#2086) reaches both: recording a verdict, and filtering + # The drift check reaches both: recording a verdict, and filtering # the list by one. A verdict an agent records has to be visible to the human # looking at the same corpus, or the two surfaces disagree about what's rotten. assert "verification" in inspect.signature(tools.list_snippets).parameters assert "verification" in inspect.getsource(routes.list_snippets_route) - # The near-duplicate report (#2088) reaches both. The web side can only hand + # The near-duplicate report reaches both. The web side can only hand # a group to the merge flow if it can get the groups in the first place. assert callable(getattr(tools, "find_duplicate_snippets", None)) assert callable(getattr(routes, "duplicate_snippets_route", None)) @@ -90,7 +89,7 @@ def test_project_scoping_reaches_every_caller(): def test_location_lookup_reaches_every_caller(): """The reverse lookup — "what already lives here?" — has to be askable from both surfaces, or an agent and a human get different answers about the same - file (rule #33).""" + file.""" from scribe.mcp.tools import snippets as tools from scribe.routes import snippets as routes from scribe.services import snippets as svc @@ -102,7 +101,7 @@ def test_location_lookup_reaches_every_caller(): def test_update_field_map_matches_service_kwargs(): """Every field the PATCH route forwards must be a real update_snippet kwarg - (rule #33 interface-contract parity).""" + (interface-contract parity).""" from scribe.routes import snippets as routes from scribe.services import snippets as svc params = inspect.signature(svc.update_snippet).parameters diff --git a/tests/test_rule_ledger_ageing.py b/tests/test_rule_ledger_ageing.py index d11fd5e8..279b1b51 100644 --- a/tests/test_rule_ledger_ageing.py +++ b/tests/test_rule_ledger_ageing.py @@ -1,13 +1,12 @@ -"""Entries in the rule exclusion ledger age out (#3751). +"""Entries in the rule exclusion ledger age out. WHY THIS EXISTS -#3749 clears the ledger when an EVENT destroys context — a compaction, a -/clear. This covers the case with no event at all: a long session where a rule -was named two hundred turns ago and has simply fallen out of attention. Same -argument #3702 made about tiers (present in context and salient at the -moment are different properties), applied to time instead. The tier itself is -gone since milestone 394; the distinction it taught is what survives. +The ledger is cleared when an EVENT destroys context — a compaction, a /clear. +This covers the case with no event at all: a long session where a rule was +named two hundred turns ago and has simply fallen out of attention. Present in +context and salient at the moment are different properties, and time +separates them as surely as a compaction does. WHAT IS PINNED, AND WHAT DELIBERATELY IS NOT @@ -76,7 +75,7 @@ def _write_ledger(ledger: Path, entries: list[tuple[str, int | None]]) -> None: def test_an_old_entry_ages_out_and_a_recent_one_does_not(tmp_path): - """BOTH DIRECTIONS IN ONE ASSERTION (rule 167), because either half alone + """BOTH DIRECTIONS IN ONE ASSERTION, because either half alone passes against a broken implementation. A test that only checks the old id is gone passes against a helper that diff --git a/tests/test_services_notes.py b/tests/test_services_notes.py index f467fc02..0f27e2b4 100644 --- a/tests/test_services_notes.py +++ b/tests/test_services_notes.py @@ -1,18 +1,17 @@ -"""services/notes.py — the inline embedding moved here from the routes (#2056). +"""services/notes.py — inline embedding lives in the service. -Why it moved: embedding was triggered at each REST route and nowhere else, so a -record created through MCP stayed out of semantic search and auto-inject until -the next restart's backfill. Putting it in the service means every caller — REST, -MCP, recurrence, snippets — gets it by construction rather than by remembering. +Embedding triggered at each REST route and nowhere else would leave a record +created through MCP out of semantic search and auto-inject until the next +restart's backfill. In the service, every caller — REST, MCP, recurrence, +snippets — gets it by construction rather than by remembering. -These test the helper directly. The point of the change is that there is now ONE -place to test. +These test the helper directly: there is ONE place to test. """ import pytest from scribe.services import notes as notes_svc -# --- inline embedding (#2056) ----------------------------------------------- +# --- inline embedding ------------------------------------------------------- def test_embed_note_uses_the_OWNER_not_the_caller(): """LOAD-BEARING for shared records. An embedding belongs to the record; a @@ -31,14 +30,14 @@ def test_embed_note_uses_the_OWNER_not_the_caller(): upsert.assert_called_once() assert upsert.call_args.args[0] == 5 assert upsert.call_args.args[1] == 42 # owner, never the caller - # Title and body travel separately since #280 — chunking happens inside + # Title and body travel separately — chunking happens inside # upsert_note_embedding, the one path every writer shares. assert upsert.call_args.args[2] == "T" assert upsert.call_args.args[3] == "B" def test_embed_note_hands_even_an_empty_record_to_the_one_path(): - """The empty-record decision moved INTO upsert_note_embedding (#280): an + """The empty-record decision lives IN upsert_note_embedding: an emptied record must have its stale vectors CLEARED, not merely skipped — so embed_note schedules the call unconditionally rather than deciding here. The clearing behaviour itself is pinned in test_chunking.py.""" @@ -73,7 +72,7 @@ def test_embed_note_swallows_an_indexing_failure(): notes_svc.embed_note(note) # must not raise -# --- #3683: a create that names a status is the transition an update is ------ +# --- a create that names a status is the transition an update is ------------- _LIFECYCLE = ("started_at", "completed_at", "recurrence_next_spawn_at") diff --git a/tests/test_services_rule_embeddings.py b/tests/test_services_rule_embeddings.py index 02e7a094..791c9138 100644 --- a/tests/test_services_rule_embeddings.py +++ b/tests/test_services_rule_embeddings.py @@ -1,9 +1,9 @@ -"""The document a rule is EMBEDDED as (milestone 307 step 4, note 3026). +"""The document a rule is EMBEDDED as. -This shape is measured, not chosen. Note 2485 probed the live corpus and found -the snippet was the only discriminative record in it — a 0.153 top-to-second -gap against 0.010-0.023 for everything else — and that the cause was its shape: -purpose stated twice in a short, single-topic document. These cases pin that +This shape is measured, not chosen. Probed across a corpus, the snippet is the +most discriminative record kind — a 0.153 top-to-second gap against +0.010-0.023 for everything else — and the cause is its shape: purpose stated +twice in a short, single-topic document. These cases pin that recipe onto rules, and pin the exclusion that matters more than any of it. """ import pytest @@ -27,8 +27,8 @@ def test_the_trigger_appears_twice_which_is_what_makes_a_vector_sharp(): def test_why_is_never_embedded(): """The exclusion this whole design turns on. `why` is dated incident - narrative — rule 46's runs to 4,300 characters — and long multi-topic prose - is what made sixteen dev-logs mutually indistinguishable: the average lands + narrative — it can run to thousands of characters — and long multi-topic + prose is what makes dev-logs mutually indistinguishable: the average lands on a centroid they all share. Adding it would not give the vector more to work with; it would give every rule the SAME thing to work with. @@ -41,7 +41,7 @@ def test_why_is_never_embedded(): def test_a_rule_with_no_trigger_still_embeds_just_less_sharply(): - """Every rule written before milestone 307 has no trigger. Degrading to + """A rule can have no trigger. Degrading to title + statement keeps them findable; it does NOT pad the document with whatever text is lying around, which would be the tempting fix and the wrong one.""" @@ -60,7 +60,7 @@ def test_an_empty_rule_yields_no_document_and_therefore_no_vector(): def test_a_long_rule_chunks_and_every_chunk_carries_the_trigger(): - """Rule 46's statement is ~3,600 characters across two headed sections. + """A ~3,600-character statement across two headed sections. The existing chunker splits it at headings and prefixes each chunk with the title — which now CONTAINS the trigger, so each half stays anchored to what the rule is for. This is why splitting a merged rule costs nothing at diff --git a/tests/test_services_snippets.py b/tests/test_services_snippets.py index 10ab94f8..aedff9d6 100644 --- a/tests/test_services_snippets.py +++ b/tests/test_services_snippets.py @@ -3,7 +3,7 @@ from scribe.services import snippets as s def test_the_embedded_title_joins_the_trigger_the_stored_one_does_not_carry(): - """Milestone 427: stored title = name; the trigger joins it at embed time, + """Stored title = name; the trigger joins it at embed time, idempotently, so an old composed title comes out the same.""" from scribe.services.embeddings import document_title @@ -167,7 +167,7 @@ def test_merge_snippet_fields_unions_locations_and_tags(): # extra tags unioned, language + "snippet" markers excluded. assert extra == ["core", "helper"] - # Attribution (#2165): only what each source ACTUALLY added. src2's location + # Attribution: only what each source ACTUALLY added. src2's location # is one the target already had, so it contributed nothing — un-merging it # must not strip a call site the survivor owns in its own right. assert contributions[0]["locations"] == [{"repo": "b", "path": "b.py", "symbol": "g"}] @@ -249,7 +249,7 @@ def test_data_and_body_round_trip_to_the_same_fields(): name, when, sig, lang = ("debounce", "rate-limit a callback", "debounce(fn, ms)", "ts") locs = [{"repo": "web", "path": "src/util.ts", "symbol": "debounce"}] - # compose_body takes no `name` — the name IS the title (milestone 427) — so + # compose_body takes no `name` — the name IS the title — so # the two serializers get their own argument lists rather than a shared spread. title = name body = s.compose_body(code="const x = 1", language=lang, signature=sig, @@ -267,7 +267,7 @@ def test_data_and_body_round_trip_to_the_same_fields(): assert from_body[key] == from_data[key], key -# --- merge provenance (#2087) ------------------------------------------------ +# --- merge provenance ------------------------------------------------------- def test_normalize_merged_from_keeps_history_order_and_drops_junk(): """Order is history, not sorting — earlier merges stay first.""" @@ -276,7 +276,7 @@ def test_normalize_merged_from_keeps_history_order_and_drops_junk(): def test_normalize_merged_from_carries_per_source_attribution(): - """The shape that makes un-merge exact (#2165): each entry holds what THAT + """The shape that makes un-merge exact: each entry holds what THAT source contributed, so reversing one can't strip what the survivor owns.""" loc = {"repo": "a", "path": "a.py", "symbol": "f"} out = s._normalize_merged_from([{"id": 7, "locations": [loc], "tags": ["x"]}])