docs(6209): coverage, guidance-ownership, shape, warnings tests and shared fixtures
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Python tests (push) Successful in 1m56s
CI & Build / Build & push image (push) Successful in 13s

test_pattern_coverage, test_guidance_ownership, test_integration_shape_classify,
test_instruction_surfaces_agree, test_retrieval_warnings, helpers.py and
conftest.py state the property each test or fixture holds. The issue and
milestone numbers, consolidation dates and one-install measurements that
recorded how each was found are dropped; fixture dates and ids stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:21:52 -04:00
co-authored by Claude Opus 5.5
parent 8518afb54d
commit 91c96a8b8a
7 changed files with 199 additions and 256 deletions
+39 -45
View File
@@ -1,4 +1,4 @@
"""Pattern-library coverage (#2692) — the extractor that mirrors the hook,
"""Pattern-library coverage — the extractor that mirrors the hook,
the shape/record matcher, and the end-to-end measurement on real Postgres.
The extractor here and the hook's awk program (scribe_prior_art.sh ARM 1)
@@ -66,11 +66,9 @@ EXTRACTION_VECTORS = [
[]),
("dedup-within-file", "def f():\n pass\ndef f():\n pass\n",
[("sym", "f")]),
# --- comment and string spans (#4222) ------------------------------------
# --- comment and string spans --------------------------------------------
# Prose is not code. A wrapped docstring line beginning "class AND the"
# announced a shape called `AND` to a live session; `with` and `nobody`
# out of one module docstring in scripts/check_dangling_styles.py reached
# persisted, judged `code_shapes` rows.
# would otherwise announce a shape called `AND`.
("docstring-prose",
'def real_one():\n """Its own text is about the\n'
' class AND the to_dict, and is a def bar():\n'
@@ -81,18 +79,15 @@ EXTRACTION_VECTORS = [
("unterminated-docstring",
'def before():\n """oops, never closed\n\ndef after():\n pass\n',
[("sym", "before"), ("sym", "after")]),
# The CSS half of the same defect (#2990): a wrapped comment line that
# The CSS half of the same rule: a wrapped comment line that
# happens to begin with a dotted token reads as a selector.
("css-comment-selector",
"/* A real base rule, not just descendants: the check reads a\n"
" .ghost, class that only ever appears as an ancestor */\n.check { }\n",
[("css", "check")]),
# The rest of #2990's verify list, which this scan satisfies as written:
# a comment holding a WHOLE rule defines nothing, and an unterminated
# comment does not swallow the file. The phantom it was filed for —
# `.editor-body` out of a wrapped comment in TaskEditorView.vue, a
# persisted row with a `used_by` count of zero inflating unused_css — is
# among the twenty-two this removes.
# A comment holding a WHOLE rule defines nothing, and an unterminated
# comment does not swallow the file. A phantom class out of a comment
# would persist with a `used_by` count of zero and inflate unused_css.
("comment-containing-a-whole-rule",
"/* .ghost {\n color: red;\n } */\n.real { color: blue; }\n",
[("css", "real")]),
@@ -195,7 +190,7 @@ def test_shapes_from_archive_strips_the_wrapper_and_gates_files():
assert shapes_from_archive(_tarball(TREE)) == TREE_SHAPES
# --- unit: template class references — the CSS consumer map (milestone 302) --
# --- unit: template class references — the CSS consumer map ----------------
def test_class_references_reads_vue_static_and_dynamic_forms_only():
@@ -229,7 +224,7 @@ def test_class_references_reads_vue_static_and_dynamic_forms_only():
def test_class_references_reads_framework_transition_names():
"""A transition `name=` is a class reference: the framework applies
`.toast-enter-active` and friends at runtime, so a stylesheet that
defines them is consumed even though no template spells one out (#2970).
defines them is consumed even though no template spells one out.
Every spelling of the tag counts; a bound `:name` stays unknowable."""
refs = class_references(
"a/T.vue",
@@ -251,7 +246,7 @@ def test_class_references_reads_framework_transition_names():
def test_class_references_reads_a_concatenated_name_as_a_prefix():
"""`status-${s}` cannot be resolved to one class, but its static head is
real information: it is emitted as the prefix reference `status-*` so
the rows it could have built are not reported unused (#2970). A head too
the rows it could have built are not reported unused. A head too
short to mean anything, or a bare separator, says nothing."""
vue = (
'<div :class="`status-${task.status}`" />'
@@ -301,7 +296,7 @@ def test_scan_archive_returns_definitions_and_references_from_one_walk():
scan = scan_archive(_tarball(tree))
assert isinstance(scan, ArchiveScan)
assert [(d.path, d.kind, d.name) for d in scan.definitions] == TREE_SHAPES + [
("web/Card.vue", "file", "Card"), # a component is a unit (milestone 439)
("web/Card.vue", "file", "Card"), # a component is a unit
("web/Card.vue", "css", "card"),
]
# Only files whose markup names a class appear; the .py/.css files don't.
@@ -309,7 +304,7 @@ def test_scan_archive_returns_definitions_and_references_from_one_walk():
assert shapes_from_archive(_tarball(tree)) == [(d.path, d.kind, d.name) for d in scan.definitions]
# --- unit: the covering predicate (lives with the ledger since #2788) --------
# --- unit: the covering predicate (lives with the ledger) --------------------
def test_location_covers_by_exact_path_dir_prefix_and_css_dot():
@@ -341,7 +336,7 @@ def test_largest_gaps_ranks_by_unclassified_and_drops_clean_dirs():
def test_type_import_specifiers_are_not_definitions():
"""#2904: `import { type Foo, bar }` is the same two words as `type Foo =`
"""`import { type Foo, bar }` is the same two words as `type Foo =`
and defines nothing; only a `type` line with a declaration after the
name counts (TS alias, Go/Rust type)."""
from scribe.services.coverage import extract_shapes
@@ -375,7 +370,7 @@ def test_coverage_line_is_evidence_carrying_and_labeled_estimate():
def test_bind_repo_tool_takes_a_ref():
"""#2873: the binding names the branch the ledger follows."""
"""The binding names the branch the ledger follows."""
from scribe.mcp.server import build_mcp_server
tool = build_mcp_server()._tool_manager.get_tool("bind_repo")
assert "ref" in tool.parameters.get("properties", {})
@@ -412,7 +407,7 @@ def _forge(tar_bytes: bytes):
def _selector(tar_bytes: bytes):
"""The keyring shape compute_coverage consumes since #2778 — one owner
"""The keyring shape compute_coverage consumes — one owner
keyring holding the mocked Gitea adapter."""
from scribe.services.forge import ForgeSelector
@@ -482,10 +477,10 @@ async def test_coverage_measures_the_tree_exactly_and_caches(seeded):
assert coverage["largest_gaps"] == [
{"dir": "src", "unclassified": 2, "total": 3}
]
# #2899: a first computation has no previous stamp — nothing is "new".
# A first computation has no previous stamp — nothing is "new".
assert coverage["derive_new"] == {"count": 0, "examples": []}
# The walk fed the LEDGER (#2788): every extracted shape has a row, the
# The walk fed the LEDGER: every extracted shape has a row, the
# snippet reference locations are mechanically stamped canonical WITH
# their snippet id, and the rest sit in the todo state.
async with async_session() as s:
@@ -516,7 +511,7 @@ async def test_coverage_measures_the_tree_exactly_and_caches(seeded):
@pytest.mark.integration
async def test_ledger_keeps_judgments_and_stamps_vanished_shapes(seeded):
"""The two survival rules (#2788): an agent's classification outlives
"""The two survival rules: an agent's classification outlives
recompute, and a shape that leaves the tree is stamped vanished — kept
for history, dropped from the readout."""
from datetime import datetime, timezone
@@ -593,9 +588,8 @@ async def test_enter_project_surfaces_the_line_only_once_computed(seeded):
@pytest.mark.integration
async def test_explicit_refresh_names_its_failures(seeded):
"""refresh_for_caller (#2802) raises fixable errors instead of silence:
an agent mid-task must learn WHY nothing measured — 'None' is exactly the
stranding the button-only path caused."""
"""refresh_for_caller raises fixable errors instead of silence: an agent
mid-task must learn WHY nothing measured, and 'None' would strand it."""
from scribe.models import async_session
from scribe.services.coverage import refresh_for_caller
@@ -617,8 +611,8 @@ async def test_explicit_refresh_names_its_failures(seeded):
@pytest.mark.integration
async def test_background_seed_is_quiet_without_a_forge(seeded):
"""refresh_if_stale (#2802) must exit silently for a forge-less owner —
rule #115's baseline — and treat a fresh cache as nothing-to-do."""
"""refresh_if_stale must exit silently for a forge-less owner — the
baseline install — and treat a fresh cache as nothing-to-do."""
from scribe.services.coverage import refresh_coverage, refresh_if_stale
uid, pid = seeded["uid"], seeded["pid"]
@@ -651,7 +645,7 @@ async def test_unservable_binding_measures_nothing(seeded):
assert await compute_coverage(uid, other_pid, selector=_selector(_tarball(TREE))) is None
# --- #2792: fingerprints and the proposer's readout --------------------------
# --- fingerprints and the proposer's readout --------------------------------
def test_extract_definitions_fingerprints_each_block():
@@ -684,14 +678,14 @@ def test_extract_definitions_fingerprints_each_block():
# And the identity view is unchanged for the hook mirror.
from scribe.services.coverage import extract_shapes
assert extract_shapes(text) == [(d.kind, d.name) for d in extract_definitions(text)]
# A CSS rule's fingerprint is its declarations (#2872): the same body
# A CSS rule's fingerprint is its declarations: the same body
# under another selector is the same shape to the derive grouping.
css = ".closed-msg {\n text-align: center;\n padding: 0.5rem 0;\n}\n.error-block {\n text-align: center;\n padding: 0.5rem 0;\n}\n.other {\n text-align: left;\n}\n"
d = {x.name: x for x in extract_definitions(css)}
assert d["closed-msg"].body_sha == d["error-block"].body_sha != d["other"].body_sha
# One-line rules hash their own declarations — never the empty string
# (first deploy grouped 68 unrelated one-liners as one copy) — and a
# SINGLE declaration is not a shape (#2903): it keeps its selector in the
# One-line rules hash their own declarations — never the empty string,
# which would group every unrelated one-liner as one copy — and a
# SINGLE declaration is not a shape: it keeps its selector in the
# hash, so `.a { color: red }` groups only with another `.a`, never with
# `.b { color: red }`. Two declarations and up stay selector-agnostic.
one = ".a { color: red; }\n\n.b { color: red; }\n\n.c { color: blue; }\n\n.a {\n color: red;\n}\n"
@@ -718,7 +712,7 @@ def test_coverage_line_names_the_proposers_standing():
assert "; 90 unclassified (40 proposed, 2 derive groups), largest: src" in line
line = coverage_line({**base, "proposed": 0, "derive_groups": [{"group": "a"}]})
assert "(1 derive group)" in line
# Milestone 302: a css top copy says what renders it; unused classes
# A css top copy says what renders it; unused classes
# join the standing block only when measured (None = no evidence).
line = coverage_line({**base, "unclassified": 0, "proposed": 0, "derive_groups": [
{"group": "name:css:error-msg", "label": ".error-msg", "files": 6,
@@ -729,7 +723,7 @@ def test_coverage_line_names_the_proposers_standing():
{"group": "name:css:x", "label": ".x", "files": 2,
"consumers": {"count": 1, "paths": ["a.vue"]}}], "unused_css": None})
assert "top copy .x ×2 files · used by 1 template" in line and "unused" not in line
# #2874: the next action on the line — biggest canon queue, widest copy.
# The next action on the line — biggest canon queue, widest copy.
line = coverage_line({
**base, "proposed": 40, "top_canon": {"snippet_id": 2844, "count": 78},
"derive_groups": [{"group": "dup:abc", "label": "closed-msg (identical body)", "files": 3}],
@@ -738,7 +732,7 @@ def test_coverage_line_names_the_proposers_standing():
def test_coverage_line_shows_standing_work_even_with_nothing_unclassified():
"""#2899: since the scoped bucket a ledger can be fully accounted and
"""A ledger can be fully accounted and
still carry derive groups / proposals / divergence — the line names
them as `standing:` instead of hiding them behind the todo count, and
names the drift since the previous refresh first-copy-first."""
@@ -789,7 +783,7 @@ def test_coverage_line_names_divergence_and_recheck():
def test_scoped_definitions_are_vue_script_setup_and_scoped_style_only():
"""#2869: one-offs by construction — every sym in a .vue and every css
"""One-offs by construction — every sym in a .vue and every css
rule inside <style scoped>; an unscoped <style> block and non-.vue files
stay ordinary."""
from scribe.services.coverage import extract_definitions, scoped_definitions
@@ -816,10 +810,10 @@ def test_scoped_definitions_are_vue_script_setup_and_scoped_style_only():
def test_svelte_scopes_by_default_and_its_module_script_stays_importable():
"""#4608: Svelte scopes the other way round from Vue — every <style> is
the component's own unless it opts out, and only <script module> /
"""Svelte scopes the other way round from Vue — every <style> is the
component's own unless it opts out, and only <script module> /
<script context="module"> can be imported elsewhere. Without this a
Svelte project's every `.card` and `.title` sat in the human todo."""
Svelte project's every `.card` and `.title` sits in the human todo."""
from scribe.services.coverage import extract_definitions, scoped_definitions
svelte = (
"<script module lang=\"ts\">\n"
@@ -847,8 +841,8 @@ def test_svelte_scopes_by_default_and_its_module_script_stays_importable():
def test_the_line_names_the_review_queue_and_the_tool_that_holds_it():
"""#4608: `stamps_to_review` existed and two projects never called it,
because nothing on arrival said there was anything to review."""
"""`stamps_to_review` goes uncalled unless something on arrival says
there is anything to review."""
from scribe.services.coverage import coverage_line
base = {"accounted": 10, "total": 10, "counts": {"exempt": 10},
"unclassified": 0, "computed_at": "2026-09-30T00:00:00"}
@@ -860,7 +854,7 @@ def test_the_line_names_the_review_queue_and_the_tool_that_holds_it():
@pytest.mark.integration
async def test_binding_ref_is_the_branch_the_ledger_follows(seeded):
"""#2873: a binding that names a ref is read at that ref (not the forge's
"""A binding that names a ref is read at that ref (not the forge's
default branch); "" clears it; None on a re-bind leaves it standing."""
from scribe.services.coverage import compute_coverage
from scribe.services.repo_bindings import bindings_for_project, set_binding
@@ -894,14 +888,14 @@ async def test_binding_ref_is_the_branch_the_ledger_follows(seeded):
("web/button.css", ".btn {\n color: red;\n}\n", False),
])
def test_a_file_is_a_unit_when_it_renders_and_nothing_inside_carries_its_name(path, text, unit):
"""Milestone 439: one structural test, not a framework list."""
"""One structural test, not a framework list."""
from scribe.services.coverage import class_references, extract_definitions, is_file_unit
assert is_file_unit(path, text, extract_definitions(text), class_references(path, text)) is unit
def test_the_line_says_whether_writers_judged_what_they_wrote():
"""Milestone 439: the write-time figure — silent until the question has
"""The write-time figure — silent until the question has
been put, then turns checked, asked, and asks walked past."""
from scribe.services.coverage import coverage_line
base = {"accounted": 10, "total": 10, "counts": {"exempt": 10},