C3: Wrapper Anchor Penalty — Design Spec
C3: Wrapper Anchor Penalty — Design Spec
§1 Problem Statement
choose_anchor picks file-level wrapper anchors (e.g., SIGNUP) over
more-specific leaf anchors (e.g., SIGNUP_HANDLE_SIGNUP) when the leaf
receives a verb-cluster mismatch penalty (-4) and the wrapper has no verb
signal (score 0).
This is the sole remaining anchor_ok failure in the patch-accuracy
benchmark: add_email_domain_check picks SIGNUP (score 0) instead of
SIGNUP_HANDLE_SIGNUP (score -4 due to CREATE↔MUTATE mismatch on
“handle”).
Impact: anchor_ok stays at 3/4 instead of the achievable 4/4.
§2 Root Cause Analysis
Wrapper anchors like SIGNUP, LOGIN, AUTH, PROFILE share a
structural pattern:
- Single token after
_path_tokens()— e.g.,{"signup"} - That token appears in every sibling anchor’s token set
- No verb token →
_classify_anchor_verbreturnsNone→ verb bonus is 0
Because verb mismatch gives -4 and the wrapper gets 0, any leaf with a
mismatched verb loses to the wrapper. This only matters when there is zero
keyword overlap (Korean request tokens vs English anchor tokens), which is
the case for add_email_domain_check.
Why not fix “handle” classification?
“handle” is classified as MUTATE, and this is load-bearing:
change_error_msg: LOGIN_HANDLE_LOGIN gets +5 from MUTATE↔MUTATE matchadd_bio_length_limit: PROFILE_HANDLE_PROFILE_UPDATE gets +5 from MUTATE↔MUTATE match (via “update”, but “handle” confirms)
Removing or reclassifying “handle” breaks these two passing scenarios.
§3 Design
§3.1 Wrapper Detection
An anchor A is a wrapper when ALL of:
- The file has ≥2 anchors
_path_tokens(A)has exactly 1 element- That element appears in
_path_tokens(other)for every other anchor in the file
This detects file-scope container anchors without relying on naming conventions or ordering.
§3.2 Penalty
_WRAPPER_ANCHOR_PENALTY = 5
Applied inside choose_anchor, after _anchor_quality_penalty:
if _is_wrapper_anchor(anchor, anchors):
score -= _WRAPPER_ANCHOR_PENALTY
§3.3 _is_wrapper_anchor helper
def _is_wrapper_anchor(anchor: str, all_anchors: list[str]) -> bool:
if len(all_anchors) < 2:
return False
tokens = _path_tokens(anchor)
if len(tokens) != 1:
return False
token = next(iter(tokens))
return all(
token in _path_tokens(other)
for other in all_anchors
if other != anchor
)
Placed immediately before choose_anchor in patch_suggester.py.
§3.4 Post-patch scoring
| Scenario | Wrapper | Wrapper score | Correct leaf | Leaf score | Winner |
|---|---|---|---|---|---|
| change_error_msg | LOGIN | 0-5 = -5 | LOGIN_HANDLE_LOGIN | +5 | ✓ leaf |
| add_email_domain_check | SIGNUP | 0-5 = -5 | SIGNUP_HANDLE_SIGNUP | -4 | ✓ leaf |
| fix_login_lock_bug | AUTH | 0-5 = -5 | AUTH_LOGIN_USER | +3 | ✓ leaf |
| add_bio_length_limit | PROFILE | 0-5 = -5 | PROFILE_HANDLE_PROFILE_UPDATE | +5 | ✓ leaf |
All 4 scenarios route to the correct anchor. Zero regressions.
§3.5 Edge cases
- Single-anchor file (e.g.,
CONFIG): condition 1 fails → no penalty. Correct: the only anchor should always be selected. - Two-token wrapper (e.g.,
APP_MAINalongsideAPP_MAIN_RUN): condition 2 fails → no penalty. Correct:APP_MAINis already specific enough to be a valid target. - Multiple wrapper-like anchors: impossible if condition 3 requires the single token to appear in ALL siblings. Only one anchor per file can satisfy this.
§4 Scope
In scope
_is_wrapper_anchorhelper function_WRAPPER_ANCHOR_PENALTYconstant- Penalty insertion in
choose_anchor - Unit tests for wrapper detection and penalty effect
- Scenario regression guard update (
add_email_domain_checkanchor tightened) - Baseline update via
vib bench --patch --update-baseline
Out of scope
- Verb cluster reclassification (no changes to
_ANCHOR_VERB_TOKENS) _apply_layer_routing(C2, already complete)add_password_change(C4, multi-fanout)- anchor_meta population in sandbox fixtures
§5 Acceptance Criteria
vib bench --patch: det/aianchor_ok == "4/4",files_ok == "5/5",recall@3 == "5/5"regressions == []- Full test suite passes (607+ tests)
test_add_email_domain_check_routes_to_signup_pagetightened fromstartswith("SIGNUP")to exact== "SIGNUP_HANDLE_SIGNUP"- New unit tests for
_is_wrapper_anchor(at least 4 cases)
§6 Files to Modify
| File | Change |
|---|---|
vibelign/core/patch_suggester.py |
Add _WRAPPER_ANCHOR_PENALTY, _is_wrapper_anchor, modify choose_anchor |
tests/test_patch_accuracy_scenarios.py |
Tighten anchor assertion |
tests/test_wrapper_anchor_penalty.py |
New: unit tests for detection + scoring |
tests/benchmark/patch_accuracy_baseline.json |
Updated via CLI |