feat(ledger): derive readout ranks body-identical groups first; CSS fingerprints are declarations, not selectors (#2872, milestone 294)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Failing after 25s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 57s
CI & Build / Build & push image (push) Successful in 32s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Failing after 25s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 57s
CI & Build / Build & push image (push) Successful in 32s
The 2026-08 audit consolidated identical bodies under different names and files (five auth views' CSS, two Workspace formatDate()s, four modal blocks) while the derive readout led with name groups (to_dict ×25, main ×5, load ×6) that were convention or coincidence. - proposal_summary ranks dup:<sha> groups above name groups, wider file spread first, and carries `files`; scoped rows (#2869) are in the readout, since that is where view-level copies live. - extract_definitions fingerprints a CSS rule by its declarations — the row identity already carries the selector — so .closed-msg / .error-block / .success-msg with one body are one dup group. One-time effect: judged css rows whose stored fingerprint predates this may show a recheck on the next refresh (the judgment stands; re-confirm). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -317,6 +317,32 @@ def test_derive_groups_copy_before_name_with_floors():
|
||||
assert ("i.py", "sym", "one") not in g
|
||||
|
||||
|
||||
def test_proposal_summary_ranks_body_identical_groups_first_and_sees_scoped_rows():
|
||||
"""#2872: dup groups (the real copies) outrank name groups (usually
|
||||
convention), wider spread first; #2869: scoped rows are in the readout."""
|
||||
from scribe.models.code_shape import CodeShape
|
||||
from scribe.services.shape_ledger import proposal_summary
|
||||
|
||||
def row(path, symbol, group, kind="css", status="scoped"):
|
||||
return CodeShape(project_id=2, repo_key="r", path=path, symbol=symbol, kind=kind,
|
||||
status=status, proposal_basis="derive", proposal_group=group)
|
||||
rows = [
|
||||
# a name group of 6 across 6 files
|
||||
*[row(f"v/{i}.vue", "status-badge", "name:css:status-badge") for i in range(6)],
|
||||
# a dup group of 3 across 3 files (different selector names, one body)
|
||||
row("v/Login.vue", "closed-msg", "dup:abc"), row("v/Reset.vue", "error-block", "dup:abc"),
|
||||
row("v/Forgot.vue", "success-msg", "dup:abc"),
|
||||
# a dup group of 2 in ONE file — a copy, but not across files
|
||||
row("v/A.vue", "x", "dup:def"), row("v/A.vue", "y", "dup:def"),
|
||||
# judged rows never count
|
||||
row("v/J.vue", "closed-msg", "dup:abc", status="exempt"),
|
||||
]
|
||||
out = proposal_summary(rows)
|
||||
assert [g["group"] for g in out["derive_groups"]] == ["dup:abc", "dup:def", "name:css:status-badge"]
|
||||
assert out["derive_groups"][0]["files"] == 3 and out["derive_groups"][0]["size"] == 3
|
||||
assert out["derive_groups"][0]["label"] == "closed-msg (identical body)"
|
||||
|
||||
|
||||
def test_confirm_requires_a_named_scope():
|
||||
import asyncio
|
||||
|
||||
|
||||
Reference in New Issue
Block a user