feat(ledger): the consumer map surfaces — every css row carries used_by on list_shapes, flag="unused-css" (the map's negative space, surfaced never deleted), derive groups and the write-path family carry consumers, the derive line says "used by N template(s)", coverage payload unused_css + the standing block; docs, SKILL, plugin 0.1.44; the two step-2 tests expected 5 edges where fan-out makes 6 (milestone 302 step 3, #2936)
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 38s
CI & Build / integration (push) Failing after 44s
CI & Build / Python tests (push) Successful in 1m26s
CI & Build / Build & push image (push) Successful in 31s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 38s
CI & Build / integration (push) Failing after 44s
CI & Build / Python tests (push) Successful in 1m26s
CI & Build / Build & push image (push) Successful in 31s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -354,6 +354,28 @@ async def consumers_of(shape_ids) -> dict[int, list[CodeShapeConsumer]]:
|
||||
return out
|
||||
|
||||
|
||||
# How many consumer files a readout names before "+N more".
|
||||
_CONSUMERS_SHOWN = 4
|
||||
|
||||
|
||||
def consumer_summary(paths: Iterable[str]) -> dict:
|
||||
"""{"count", "paths"} — distinct consumer files, sorted, the first few
|
||||
named. The one shape every surface uses for "used by N template(s)"."""
|
||||
files = sorted(set(paths))
|
||||
return {"count": len(files), "paths": files[:_CONSUMERS_SHOWN]}
|
||||
|
||||
|
||||
async def used_by_map(rows: Iterable[CodeShape]) -> dict[int, dict]:
|
||||
"""{shape_id: consumer_summary} for every css row given — a row with no
|
||||
consumer gets {"count": 0, "paths": []}: "no template names it" is a
|
||||
finding, not an absence."""
|
||||
css = [r for r in rows if r.kind == "css"]
|
||||
if not css:
|
||||
return {}
|
||||
edges = await consumers_of([r.id for r in css])
|
||||
return {r.id: consumer_summary(e.path for e in edges.get(r.id, [])) for r in css}
|
||||
|
||||
|
||||
async def mark_canonicals(
|
||||
project_id: int, recorded: list[tuple[int, str, str]]
|
||||
) -> None:
|
||||
@@ -666,7 +688,8 @@ async def list_project_shapes(
|
||||
suggestion), "derive" (a repeats-with-no-canon group), or one basis
|
||||
name (symbol/reference/text/signature/semantic). ``flag`` narrows to
|
||||
the readout's asks (#2793): "divergence" (new where a canon dominates,
|
||||
`diverges_from` names it) or "recheck" (a judged shape whose body moved).
|
||||
`diverges_from` names it), "recheck" (a judged shape whose body moved),
|
||||
or "unused-css" (milestone 302: a css rule no template names).
|
||||
"""
|
||||
from sqlalchemy import func, or_
|
||||
|
||||
@@ -701,6 +724,13 @@ async def list_project_shapes(
|
||||
conds.append(CodeShape.diverges_from.isnot(None))
|
||||
elif flag == "recheck":
|
||||
conds.append(CodeShape.recheck_at.isnot(None))
|
||||
elif flag == "unused-css":
|
||||
# The consumer map's negative space (milestone 302): a live css rule
|
||||
# no file's markup names. A candidate for deletion, surfaced — never
|
||||
# deleted — because the map reads templates only (a class built at
|
||||
# runtime, or used from a script, is invisible to it).
|
||||
conds.append(CodeShape.kind == "css")
|
||||
conds.append(~CodeShape.id.in_(select(CodeShapeConsumer.shape_id)))
|
||||
if uses:
|
||||
# Consumers of a canon (#2870): rows with a uses edge to it, whatever
|
||||
# shape they themselves are.
|
||||
@@ -1461,13 +1491,21 @@ async def apply_derive_groups(project_id: int) -> int:
|
||||
return grouped
|
||||
|
||||
|
||||
def proposal_summary(rows: Iterable[CodeShape], *, top: int = 8) -> dict:
|
||||
def proposal_summary(
|
||||
rows: Iterable[CodeShape], *, top: int = 8,
|
||||
consumer_paths: dict[int, list[str]] | None = None,
|
||||
) -> dict:
|
||||
"""The readout's view of the proposer's standing: how many canon
|
||||
proposals await confirmation, and the largest derive-first groups."""
|
||||
proposals await confirmation, and the largest derive-first groups.
|
||||
``consumer_paths`` (shape_id → files whose markup names it, milestone
|
||||
302) puts `consumers` on each group — the family's distinct consumer
|
||||
files across its members, the datum that separates a shared recipe
|
||||
from a scoped convention."""
|
||||
proposed = 0
|
||||
by_canon: dict[int, int] = {}
|
||||
groups: dict[str, dict] = {}
|
||||
files: dict[str, set[str]] = {}
|
||||
consumers: dict[str, set[str]] = {}
|
||||
for row in rows:
|
||||
if row.status not in _MECHANICAL_TODO:
|
||||
continue
|
||||
@@ -1488,8 +1526,14 @@ def proposal_summary(rows: Iterable[CodeShape], *, top: int = 8) -> dict:
|
||||
files.setdefault(row.proposal_group, set()).add(row.path)
|
||||
if len(g["paths"]) < 3:
|
||||
g["paths"].append(row.path)
|
||||
if consumer_paths is not None and row.kind == "css":
|
||||
consumers.setdefault(row.proposal_group, set()).update(
|
||||
consumer_paths.get(row.id) or ()
|
||||
)
|
||||
for key, g in groups.items():
|
||||
g["files"] = len(files[key])
|
||||
if key in consumers:
|
||||
g["consumers"] = consumer_summary(consumers[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,
|
||||
@@ -1753,15 +1797,21 @@ async def write_time_derive(
|
||||
group = grouped[0].proposal_group
|
||||
members = [r for r in grouped if r.proposal_group == group]
|
||||
files = sorted({r.path for r in members})
|
||||
out.append({
|
||||
"symbol": name, "kind": kind, "key": group,
|
||||
"family": {
|
||||
"group": group, "label": label,
|
||||
"identical": not group.startswith("name:"),
|
||||
"files": files[:_DERIVE_FILES_SHOWN], "file_count": len(files),
|
||||
"size": len(members) + (1 if here is not None else 0),
|
||||
},
|
||||
})
|
||||
family = {
|
||||
"group": group, "label": label,
|
||||
"identical": not group.startswith("name:"),
|
||||
"files": files[:_DERIVE_FILES_SHOWN], "file_count": len(files),
|
||||
"size": len(members) + (1 if here is not None else 0),
|
||||
}
|
||||
if kind == "css":
|
||||
# What renders the family (milestone 302): the members' consumer
|
||||
# files, the row at `path` included when it already exists.
|
||||
ids = [r.id for r in members] + ([here.id] if here is not None else [])
|
||||
edges = await consumers_of(ids)
|
||||
family["consumers"] = consumer_summary(
|
||||
e.path for es in edges.values() for e in es
|
||||
)
|
||||
out.append({"symbol": name, "kind": kind, "key": group, "family": family})
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user