"""The shape ledger's write side — sync and mechanical marking (#2788). The coverage walk (services/coverage.py) is the only feed that sees every shape, so it is the ledger's sync point: each refresh upserts one repo's extracted shapes — new shapes arrive `unclassified` (THE todo state, note 2786), surviving shapes bump their last-seen marker, vanished shapes get stamped rather than deleted (history is the point). Classifications survive recompute by construction: the upsert never touches a judgment, with two deliberate exceptions — - a judgment whose snippet target is gone (SET NULL on snippet deletion) is re-filed as unclassified so it rejoins the todo instead of dangling; - a MECHANICALLY-stamped canonical row whose snippet location no longer covers it falls back to unclassified. Only mechanical stamps self-heal; an agent's judgment is never unwound by machinery. `location_covers` is the one covering predicate — the same doctrine the recorded-location drift check uses — shared by the sync's canonical marking and by anything else that must decide whether a recorded location speaks for an extracted shape. """ from __future__ import annotations import difflib import logging import re from datetime import datetime, timedelta, timezone from typing import Iterable, NamedTuple from sqlalchemy import select from scribe.models import async_session from scribe.models.code_shape import REASON_CODES, CodeShape, CodeShapeEvent, CodeShapeUse from scribe.models.base import iso logger = logging.getLogger(__name__) # Statuses whose meaning requires a snippet target. _NEEDS_TARGET = ("canonical", "instance", "variant") def _norm_symbol(kind_or_symbol: str) -> str: # CSS shapes and recorded CSS symbols may or may not carry the leading # dot; compare without it so ".btn-primary" and "btn-primary" agree. return kind_or_symbol.lstrip(".").strip() def location_covers(loc_path: str, loc_symbol: str, path: str, name: str) -> bool: """Does a recorded (path, symbol) location speak for this shape? Symbol-less locations never cover a shape — a whole-file record makes no claim about any particular definition inside it. Path semantics are the drift check's own: exact file, or the recorded path is a directory the file lives under. """ if not (loc_symbol or "").strip(): return False if _norm_symbol(loc_symbol) != _norm_symbol(name): return False if not loc_path: # Symbol-only record: the symbol match is all the claim there is. return True from scribe.services.snippets import _path_touches return _path_touches(loc_path, path) # The rows the machine may still speak about: nobody's judgment stands on # them. `scoped` (#2869) is the sync's own by-construction stamp — the # proposer, derive grouping, divergence, hook evidence and sweeps treat it # like the todo; only the human todo (`unclassified`) excludes it. _MECHANICAL_TODO = ("unclassified", "scoped") _SCOPED_REASON = ( "by construction: a Vue component's scoped