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:
@@ -185,8 +185,15 @@ def extract_definitions(text: str) -> list[Definition]:
|
||||
end = j
|
||||
break
|
||||
block = lines[i:end]
|
||||
# A CSS rule's fingerprint is its DECLARATIONS, not its selector
|
||||
# (#2872): the row's identity already carries the selector, and the
|
||||
# question the fingerprint answers for derive grouping is "is this the
|
||||
# same rule under another name?" — .closed-msg / .error-block /
|
||||
# .success-msg with identical bodies are one dup group, not three
|
||||
# lonely rows. Sym blocks keep their signature line in the hash.
|
||||
hashed = block[1:] if kind == "css" and len(block) > 1 else block
|
||||
out.append(Definition(
|
||||
kind, name, lines[i].strip()[:_SIGNATURE_CAP], _block_sha(block),
|
||||
kind, name, lines[i].strip()[:_SIGNATURE_CAP], _block_sha(hashed),
|
||||
"\n".join(block), i,
|
||||
))
|
||||
return out
|
||||
|
||||
@@ -1215,25 +1215,36 @@ def proposal_summary(rows: Iterable[CodeShape], *, top: int = 8) -> dict:
|
||||
proposals await confirmation, and the largest derive-first groups."""
|
||||
proposed = 0
|
||||
groups: dict[str, dict] = {}
|
||||
files: dict[str, set[str]] = {}
|
||||
for row in rows:
|
||||
if row.status != "unclassified":
|
||||
if row.status not in _MECHANICAL_TODO:
|
||||
continue
|
||||
if row.proposed_snippet_id is not None:
|
||||
proposed += 1
|
||||
elif row.proposal_group:
|
||||
dup = not row.proposal_group.startswith("name:")
|
||||
g = groups.setdefault(row.proposal_group, {
|
||||
"group": row.proposal_group, "kind": row.kind,
|
||||
"label": (
|
||||
("." if row.kind == "css" else "") + row.symbol
|
||||
if row.proposal_group.startswith("name:")
|
||||
else f"{row.symbol} (identical body)"
|
||||
f"{row.symbol} (identical body)" if dup
|
||||
else ("." if row.kind == "css" else "") + row.symbol
|
||||
),
|
||||
"size": 0, "paths": [],
|
||||
"size": 0, "files": 0, "paths": [],
|
||||
})
|
||||
g["size"] += 1
|
||||
files.setdefault(row.proposal_group, set()).add(row.path)
|
||||
if len(g["paths"]) < 3:
|
||||
g["paths"].append(row.path)
|
||||
ranked = sorted(groups.values(), key=lambda g: (-g["size"], g["group"]))
|
||||
for key, g in groups.items():
|
||||
g["files"] = len(files[key])
|
||||
# Body-identical groups first (#2872): the things an audit actually
|
||||
# consolidated were identical bodies under different names/files; a
|
||||
# name repeated across modules is usually convention. Within a tier,
|
||||
# the group spread over more files is the bigger copy.
|
||||
ranked = sorted(
|
||||
groups.values(),
|
||||
key=lambda g: (g["group"].startswith("name:"), -g["files"], -g["size"], g["group"]),
|
||||
)
|
||||
return {"proposed": proposed, "derive_groups": ranked[:top]}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user