"""The shape ledger's write side — sync and mechanical marking. 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 posixpath import re from datetime import datetime, timedelta, timezone from typing import Iterable, Mapping, NamedTuple from sqlalchemy import select from grimoire.models import async_session from grimoire.models.code_shape import ( REASON_CODES, CodeShape, CodeShapeConsumer, CodeShapeEvent, CodeShapeUse, ) from grimoire.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 grimoire.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` 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