fix(ledger): proposer precision — own-definition signatures, semantic floor 0.8, ruleset version #118

Merged
bvandeusen merged 1 commits from dev into main 2026-08-21 08:27:28 -04:00
+23 -10
View File
@@ -710,6 +710,14 @@ _DERIVE_MIN_NAME = 3
# Semantic checks per repo per refresh — an embedding each (local fastembed), # Semantic checks per repo per refresh — an embedding each (local fastembed),
# bounded so a 4,000-row ledger is worked through over refreshes, not in one. # bounded so a 4,000-row ledger is worked through over refreshes, not in one.
_SEMANTIC_CAP = 150 _SEMANTIC_CAP = 150
# The semantic basis's own floor — stricter than the write-path arm's, because
# a code BODY against a prose-forward snippet document scores 0.680.75 for
# "both are about migrations"; first live run paired every alembic
# upgrade()/downgrade() with an unrelated canon at exactly that band.
_SEMANTIC_FLOOR = 0.8
# Bump when a basis's rule changes: rows remember the (body, ruleset) they
# were examined under, so a tightened rule re-examines everything once.
_PROPOSER_VERSION = 2
# Signature resemblance floor, name blanked (difflib ratio) — and a length # Signature resemblance floor, name blanked (difflib ratio) — and a length
# floor, because `def NAME():` resembles `def NAME(x):` at 0.95 while saying # floor, because `def NAME():` resembles `def NAME(x):` at 0.95 while saying
# nothing; a family shape has parameters to resemble. # nothing; a family shape has parameters to resemble.
@@ -823,13 +831,16 @@ async def canon_catalog(user_id: int) -> list[Canon]:
symbol = (fields.get("symbol") or "").strip() symbol = (fields.get("symbol") or "").strip()
kind = snippet_kind(symbol, fields.get("language") or "") kind = snippet_kind(symbol, fields.get("language") or "")
code = fields.get("code") or "" code = fields.get("code") or ""
defs = extract_definitions(code) # The signature basis matches against the canon's OWN definition
signature = "" # line — never against an example in its code. A snippet whose code
if defs: # opens with a call-site example (confirmed()'s does) would otherwise
own = next( # make every `async function x(): Promise<void>` resemble it.
(d for d in defs if _norm_symbol(d.name) == _norm_symbol(symbol)), defs[0] own = next(
) (d for d in extract_definitions(code)
signature = own.signature if symbol and _norm_symbol(d.name) == _norm_symbol(symbol)),
None,
)
signature = own.signature if own else ""
out.append(Canon( out.append(Canon(
int(note.id), kind, symbol, int(note.id), kind, symbol,
tuple( tuple(
@@ -867,7 +878,8 @@ async def _semantic_canon(
return None return None
query = concept_query(body) or body query = concept_query(body) or body
hits = await semantic_search_notes( hits = await semantic_search_notes(
user_id, query, limit=3, threshold=WRITEPATH_DEFAULT_THRESHOLD, user_id, query, limit=3,
threshold=max(WRITEPATH_DEFAULT_THRESHOLD, _SEMANTIC_FLOOR),
note_type="snippet", scope="browse", note_type="snippet", scope="browse",
) )
for score, note in hits: for score, note in hits:
@@ -914,7 +926,8 @@ async def propose_for_repo(
if d is None: if d is None:
continue continue
signature, body_sha, body = d[3], d[4], d[5] signature, body_sha, body = d[3], d[4], d[5]
if row.proposed_at is not None and row.proposed_sha == body_sha: examined_as = f"{body_sha}@{_PROPOSER_VERSION}"
if row.proposed_at is not None and row.proposed_sha == examined_as:
continue continue
examined += 1 examined += 1
group = row.proposal_group # derive grouping is reassigned below group = row.proposal_group # derive grouping is reassigned below
@@ -925,7 +938,7 @@ async def propose_for_repo(
_clear_proposal(row) _clear_proposal(row)
row.proposal_group = group row.proposal_group = group
row.proposed_at = now row.proposed_at = now
row.proposed_sha = body_sha row.proposed_sha = examined_as
if hit: if hit:
row.proposed_snippet_id, row.proposal_basis, row.proposal_score = hit row.proposed_snippet_id, row.proposal_basis, row.proposal_score = hit
row.proposal_group = None row.proposal_group = None