fix(shapes): the signature basis admits the band true instances score in (#4306)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 57s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 27s

Measured over 459 judged instance rows against every same-family canon:
true instances score 1.0 or ~0.795 (a model whose mixin list differs from
the canon's), and 0.8 cut the second band entirely. The proposal is read
before it is confirmed, so the floor admits it: 0.75 takes every measured
true row, at 35 wrong-canon pairings instead of 14.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
2026-09-22 08:44:58 -04:00
co-authored by Claude Opus 5
parent 312dc9f6f7
commit a01deeb851
2 changed files with 28 additions and 2 deletions
+12 -2
View File
@@ -1570,12 +1570,22 @@ def _semantic_priority(row) -> tuple:
# not evidence (see _SEMANTIC_LIMIT) — but the proposals themselves did not
# change, so no re-examination is owed; a stale miss basis is inert.
# v5: the semantic arm's floor dropped from 0.8 to the write-path floor (#4208),
# so rows it examined and found nothing for must be read once more.
# and the signature floor from 0.8 to 0.75 (#4306), so rows examined and
# found nothing for must be read once more.
_PROPOSER_VERSION = 5
# 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.
_SIGNATURE_FLOOR = 0.8
#
# A proposal is read before it is confirmed, so this admits the band where
# true instances live rather than the band where nothing is wrong (#4306).
# Measured 2026-09-22 over 459 judged instance rows against every same-family
# canon: true instances score 1.0 or ~0.795 (a model class whose mixin list
# differs from the canon's), and 0.8 cut the whole second band. At 0.75 all
# of them clear; the price is `async def NAME(user_id: int, …) -> dict`
# look-alikes (35 wrong-canon pairings vs 14 at 0.8), which read as wrong at
# a glance. Below 0.75 the next true row is at 0.6 behind 150+ wrong ones.
_SIGNATURE_FLOOR = 0.75
_SIGNATURE_MIN_LEN = 30
# Textual containment needs enough substance to mean anything.
_TEXT_FLOOR = 40
+16
View File
@@ -180,6 +180,22 @@ def test_signature_similarity_blanks_the_names():
assert sim("def helper(x):", "helper", "def make_app():", "make_app") == 0.0
def test_a_model_with_a_different_mixin_list_is_proposed_against_the_model_canon():
"""The band 0.8 cut (#4306). Measured over judged instances: a model class
whose mixins differ from the canon's scores ~0.795 against it — a true
instance, and the proposal is read before it is confirmed, so the floor
must admit it."""
from scribe.services.shape_ledger import Canon, _norm_text, match_canon
model = Canon(44, "sym", "Gadget", (("src/app/models/gadget.py", "Gadget"),),
"class Gadget(Base, TimestampMixin, SoftDeleteMixin):",
_norm_text(""), 0, "python")
hit = match_canon("sym", "src/app/models/widget.py", "Widget",
"class Widget(Base, TimestampMixin):", " pass", [model])
assert hit is not None and hit[:2] == (44, "signature")
assert 0.75 <= hit[2] < 0.8
def test_text_containment_is_whitespace_insensitive_with_a_floor():
from scribe.services.shape_ledger import text_contains