docs(6209): twelve more test files say which behaviour they pin
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 12s

shape ledger, MCP projects/search/rulebooks tools, backup, chunking,
plugin context, derived mirror, rule band, shape form gate, plugin
version mint and note usage tests drop issue, milestone and rule numbers,
dates and one-install figures from docstrings, comments and messages.
Three shape-form-gate test names that told the discovery story now name
the property: a registry module's helpers are not service functions, the
divergence gate silences only a family contradiction, and callable helpers
beside a callable canon still prompt. The rulebook tool-count comment that
logged each milestone's additions is replaced by the reason it is a count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:24:50 -04:00
co-authored by Claude Opus 5.5
parent 91c96a8b8a
commit d41e72d26c
12 changed files with 223 additions and 265 deletions
+16 -18
View File
@@ -52,7 +52,7 @@ async def test_fable_search_returns_repackaged_results():
@pytest.mark.asyncio
async def test_search_shows_the_passage_that_matched_not_the_opening():
"""The point of #4243. A record can rank on its sixth paragraph; showing
"""A record can rank on its sixth paragraph; showing
its first is showing the caller a span the search already judged less
relevant, and letting them decide from it."""
_user_id_ctx.set(7)
@@ -131,7 +131,7 @@ async def test_fable_search_content_type_filters_at_service_layer():
specific kinds arrived — it is BROAD here (any non-task, snippets and
lessons included) while the web facet of the same name is narrow. Pinning
it stops a later tidy-up from silently removing snippets from every caller
that already asks this way (#4250)."""
that already asks this way."""
_user_id_ctx.set(7)
mock_search = AsyncMock(return_value=[])
with patch("scribe.mcp.tools.search.semantic_search_notes", mock_search):
@@ -149,7 +149,7 @@ async def test_fable_search_content_type_filters_at_service_layer():
assert mock_search.call_args.kwargs["is_task"] is None
# --- the specific kinds: the engine already supported them (#4250) -----------
# --- the specific kinds the engine supports ----------------------------------
@pytest.mark.asyncio
@@ -181,9 +181,8 @@ async def test_each_specific_kind_reaches_the_engine_filter_it_names(
def test_the_vocabulary_is_derived_from_the_facet_table_not_recopied():
"""#3161's property, held at this door too: adding a kind to `_FACETS` is
one edit. A hand-kept list here is exactly how the agent's search came to
offer two kinds while the web's offered nine."""
"""Adding a kind to `_FACETS` is one edit, at this door too. A hand-kept
list here lets the agent's search offer fewer kinds than the web's."""
from scribe.services.knowledge import FACET_TYPES, content_type_filters
for facet in FACET_TYPES:
@@ -191,7 +190,7 @@ def test_the_vocabulary_is_derived_from_the_facet_table_not_recopied():
assert "is_task" in filters, facet
# The guard can fail: a name absent from the table is refused, so this is
# membership in the table and not "every string works" (#167).
# membership in the table and not "every string works".
with pytest.raises(ValueError):
content_type_filters("a-kind-that-is-not-in-the-facet-table")
@@ -215,7 +214,7 @@ async def test_an_unknown_content_type_is_refused_rather_than_returning_nothing(
@pytest.mark.asyncio
async def test_the_docstring_names_every_kind_the_tool_accepts():
"""The docstring IS the agent-facing contract (#2846) — a filter an agent
"""The docstring IS the agent-facing contract — a filter an agent
has not been told about is unreachable however well it is wired."""
from scribe.mcp.tools.search import search as search_tool
from scribe.services.knowledge import FACET_TYPES
@@ -229,12 +228,12 @@ async def test_the_docstring_names_every_kind_the_tool_accepts():
@pytest.mark.asyncio
async def test_an_explicit_search_reaches_a_lesson_from_any_project():
"""The wiring half of milestone 385 step 3.
"""An explicit search reaches lessons in every project.
A lesson records an insight that transfers, so a project filter that hid it
would hide it precisely on the project that has not learned it yet. This is
the EXPLICIT search — the operator asked — so it opts in; the unasked-for
injection arms decide their own budget separately (step 5).
injection arms decide their own budget separately.
Asserted on the kwarg rather than on results, because what can regress here
is the wiring: the service grew the capability and a call site that never
@@ -269,7 +268,7 @@ async def test_fable_search_limit_is_clamped():
(0, {"everywhere": True}),
])
async def test_rule_search_scopes_to_the_project_it_is_given(project_id, scope):
"""With a project: global rules plus that project's (milestone 414).
"""With a project: global rules plus that project's.
Without one: every rule, because an unscoped "is there a rule about this"
is asking the whole rulebook — unlike a hook, which speaks unasked."""
_user_id_ctx.set(7)
@@ -293,14 +292,13 @@ def test_a_milestone_is_embedded_by_what_it_is_for_then_its_plan():
@pytest.mark.asyncio
async def test_milestone_search_is_its_own_shape_and_scopes_to_the_project():
"""milestone 415: 'is there already a plan for this?' has a tool."""
"""'Is there already a plan for this?' has a tool."""
from unittest.mock import MagicMock
_user_id_ctx.set(7)
# `body` is a REAL string, not left to MagicMock's attribute autovivication:
# the result now reads it, and a mock body would make `matched` a mock
# object and `body_length` zero while the assertion still looked green
# (lesson #2833).
# object and `body_length` zero while the assertion still looked green.
ms = MagicMock(id=339, title="M3 — Metadata", description="works, editions",
status="active", project_id=30,
body="Step 4 — the metadata editions table.")
@@ -323,7 +321,7 @@ async def test_milestone_search_is_its_own_shape_and_scopes_to_the_project():
assert out["results"] == [{
"id": 339, "title": "M3 — Metadata", "description": "works, editions",
# The plan body stays out; the passage that matched comes along, and
# says which of the two it is (#4243).
# says which of the two it is.
"matched": "Step 4 — the metadata editions table.",
"matched_is": "matched_passage",
"body_length": len("Step 4 — the metadata editions table."),
@@ -332,7 +330,7 @@ async def test_milestone_search_is_its_own_shape_and_scopes_to_the_project():
}]
# --- systems: the charter as an answer, not a filter (#4251) -----------------
# --- systems: the charter as an answer, not a filter -------------------------
@pytest.mark.asyncio
@@ -340,7 +338,7 @@ async def test_system_search_is_its_own_shape_and_carries_the_matched_passage():
"""A System's charter runs to several hundred words and a result shows its
NAME — so a match on the paragraph that actually decides where a record
belongs would be previewed by two words that cannot. The passage comes
along, marked as the passage (#4243)."""
along, marked as the passage."""
_user_id_ctx.set(7)
charter = (
"Opening sentence about the area in general. "
@@ -349,7 +347,7 @@ async def test_system_search_is_its_own_shape_and_carries_the_matched_passage():
)
fake = MagicMock(id=3, project_id=2, description=charter)
# `name` is MagicMock's own constructor kwarg — passed in, it names the
# mock and leaves `.name` a mock object, which is note #2833's whole point.
# mock and leaves `.name` a mock object.
fake.name = "Retrieval & recall"
async def _system_search(uid, q, **kwargs):