feat(ledger): reason codes, case-insensitive repo filter, next action on the coverage line (#2874, milestone 294)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Failing after 25s
CI & Build / TypeScript typecheck (push) Canceled after 32s
CI & Build / Python tests (push) Canceled after 31s
CI & Build / Build & push image (push) Canceled after 0s

- code_shapes.reason_code (migration 0083): an optional code from a fixed
  catalogue (scoped-css, one-off-handler, test-helper, convention-plumbing,
  pure-helper, generated, script, typed-record) beside the prose reason, so
  the ledger can be filtered/aggregated by kind of one-off; validated in
  classify_shapes and classify_shapes_by_rule; on to_dict/to_compact.
- Snippet location lookups match repo case-insensitively in both dialects
  (location_matches / location_jsonpath via like_regex flag "i") — "Scribe"
  vs "FabledScribe" vs "fabledscribe" recorded free-form hid half the canon
  from list_snippets(repo=, path=).
- coverage line names the next action: "top canon #N ×k" (biggest proposal
  queue) and "top copy <label> ×files" (widest body-identical group).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 15:14:34 -04:00
co-authored by Claude Fable 5
parent d01201539b
commit 1a8e5787e8
9 changed files with 123 additions and 11 deletions
+18
View File
@@ -26,6 +26,20 @@ from scribe.models.base import TimestampMixin, iso
SHAPE_STATUSES = ("canonical", "instance", "variant", "exempt", "scoped", "unclassified")
SHAPE_CLASSIFIERS = ("agent", "audit", "hook", "mechanical", "import")
# The reason catalogue (#2874): an OPTIONAL code beside the prose reason on
# variant/exempt rows, so the ledger can be filtered and aggregated by kind
# of one-off. The prose remains the record; the code is the index.
REASON_CODES = (
"scoped-css", # a scoped rule styling one element (pre-#2869 rows)
"one-off-handler", # a view/component handler or loader, one per surface
"test-helper", # a test module's stub, driver or fixture data
"convention-plumbing", # registration, wiring, app factory — one of each
"pure-helper", # a sync module-private helper with no session
"generated", # generated source (theme.css, protos, bundles)
"script", # a standalone dev/CI script
"typed-record", # a NamedTuple / dataclass / error class — one each
)
# How the mechanical proposer (#2792) arrived at a proposal, strongest first.
# `derive` is the odd one out: not "this is an instance of #N" but "this
# shape repeats with NO canon — derive one first" (note 2786's derive-first
@@ -105,6 +119,7 @@ class CodeShape(Base, TimestampMixin):
BigInteger, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True
)
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
reason_code: Mapped[str | None] = mapped_column(Text, nullable=True) # REASON_CODES (#2874)
classified_by: Mapped[str | None] = mapped_column(Text, nullable=True)
classified_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
@@ -158,6 +173,7 @@ class CodeShape(Base, TimestampMixin):
"status": self.status,
"snippet_id": self.snippet_id,
"reason": self.reason,
"reason_code": self.reason_code,
"classified_by": self.classified_by,
"classified_at": iso(self.classified_at),
"first_seen_commit": self.first_seen_commit,
@@ -189,6 +205,8 @@ class CodeShape(Base, TimestampMixin):
out["snippet_id"] = self.snippet_id
if self.classified_by:
out["by"] = self.classified_by
if self.reason_code:
out["reason_code"] = self.reason_code
proposal = self.proposal
if proposal:
out["proposal"] = proposal