feat(ledger): audit surfaces — list_shapes(compact=True) and classify_shapes_by_rule, the sweep form of a judgment (#2868, milestone 294)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Failing after 25s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 1m0s
CI & Build / Build & push image (push) Successful in 26s

The 2026-08 audit judged 3,427 rows in 14 hand-driven batches through a raw
MCP client because a list_shapes page overflowed the tool budget and every
row had to be sent back one by one. Now:

- list_shapes(compact=True): path · symbol · kind · status · signature, plus
  snippet_id / by / proposal / diverges_from / recheck only when set. A full
  500-row page fits. CodeShape.to_compact() is the row shape.
- classify_shapes_by_rule(project_id, path, status, pattern=, kind=,
  snippet_id=, reason=, via=, include_judged=): ONE judgment over every
  unclassified live row under a directory whose symbol matches a glob;
  judged rows are untouched unless include_judged; canonical is refused;
  same gates as the row form; one transaction; returns count + sample.
  shape_ledger.classify_shapes_where / rule_matches (pure) carry it.

Tests: compact row pinned, rule_matches directory/glob/kind semantics, the
tool mount, and an integration sweep (unclassified-only, include_judged,
gates).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 15:02:55 -04:00
co-authored by Claude Fable 5
parent 3a4031d7f8
commit 9abc4443fb
5 changed files with 255 additions and 3 deletions
+25
View File
@@ -167,6 +167,31 @@ class CodeShape(Base, TimestampMixin):
"updated_at": iso(self.updated_at),
}
def to_compact(self) -> dict:
"""The row as an audit reads it (#2868): identity, standing, the
definition line and the proposer's word — none of the bookkeeping
(commits, shas, timestamps). A 500-row page of these fits the tool
budget; a page of to_dict() does not."""
out = {
"path": self.path,
"symbol": self.symbol,
"kind": self.kind,
"status": self.status,
"signature": self.signature,
}
if self.snippet_id is not None:
out["snippet_id"] = self.snippet_id
if self.classified_by:
out["by"] = self.classified_by
proposal = self.proposal
if proposal:
out["proposal"] = proposal
if self.diverges_from is not None:
out["diverges_from"] = self.diverges_from
if self.recheck_at is not None:
out["recheck"] = True
return out
# What a shape's history records (#2793). Not "appeared" — first_seen and
# created_at already say that on the row; history is for what CHANGED: