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),
# bounded so a 4,000-row ledger is worked through over refreshes, not in one.
_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
# floor, because `def NAME():` resembles `def NAME(x):` at 0.95 while saying
# 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()
kind = snippet_kind(symbol, fields.get("language") or "")
code = fields.get("code") or ""
defs = extract_definitions(code)
signature = ""
if defs:
own = next(
(d for d in defs if _norm_symbol(d.name) == _norm_symbol(symbol)), defs[0]
)
signature = own.signature
# The signature basis matches against the canon's OWN definition
# line — never against an example in its code. A snippet whose code
# opens with a call-site example (confirmed()'s does) would otherwise
# make every `async function x(): Promise<void>` resemble it.
own = next(
(d for d in extract_definitions(code)
if symbol and _norm_symbol(d.name) == _norm_symbol(symbol)),
None,
)
signature = own.signature if own else ""
out.append(Canon(
int(note.id), kind, symbol,
tuple(
@@ -867,7 +878,8 @@ async def _semantic_canon(
return None
query = concept_query(body) or body
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",
)
for score, note in hits:
@@ -914,7 +926,8 @@ async def propose_for_repo(
if d is None:
continue
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
examined += 1
group = row.proposal_group # derive grouping is reassigned below
@@ -925,7 +938,7 @@ async def propose_for_repo(
_clear_proposal(row)
row.proposal_group = group
row.proposed_at = now
row.proposed_sha = body_sha
row.proposed_sha = examined_as
if hit:
row.proposed_snippet_id, row.proposal_basis, row.proposal_score = hit
row.proposal_group = None