fix(rules): a shortened rule line must not decide what it says about holding (#3851)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 40s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m24s
CI & Build / Build & push image (push) Successful in 26s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 40s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m24s
CI & Build / Build & push image (push) Successful in 26s
CI run 6485 was red. Six failures, three causes, and only one of them was a stale test. THE REAL DEFECT. The compact branch dropped the `seen` TAIL along with the trigger, so a rule the session had already been told rendered exactly like one it had not. #3750's whole argument is that those are different claims — a repeat is rendered precisely because the session may no longer HOLD what it was told — and the tail is the entire difference a reader can act on. test_a_rule_the_session_already_holds_is_referenced_not_re_offered caught it within one commit, which is that guard working as intended. Fixed by keeping the tail and dropping only the trigger, which is both the cheaper and the safer cut: a trigger runs 300-400 characters after #3855, a tail about 100. Re-measured on the real renderer — top-full-plus-references is ~299 tokens against ~568 for five full lines, so about 2x the old single line rather than the 1.4x claimed before, for four more rules and no lost information. The comments carrying the old figure are corrected rather than left to read as a decision nobody made. THE FIXTURE THAT STRADDLED THE BAND. `_THREE_HITS` spanned 0.81-0.74 against a 0.05 band, so the act arms dropped its lowest hit and four cases of test_both_recorders_report_the_same_rules_for_one_call failed reporting a count mismatch — under a message blaming the exclusion filter. A guard pointing confidently at the wrong subsystem costs more than no guard, because it is believed. Scores retightened to 0.81/0.80/0.79 and the precondition is now asserted by a named test, so a future band change is told where the problem is instead of through four confusing failures. THE STALE CONSTANT GUARD. test_the_rule_arm_asks_for_one_rule_not_two pinned RULEHINT_LIMIT == 1 — a real decision, correctly guarded, for a world with a resident set. Rewritten to pin what replaced it, as relationships rather than values (rule 115): the arm can return several, and rules are narrowed HARDER than the notes menu because they measured flatter, not sharper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
This commit is contained in:
@@ -221,8 +221,12 @@ _RULEHINT_BAND = 0.05
|
||||
# a fourth voice which speaks at full volume twice is where a reader stops
|
||||
# reading — is the half worth keeping, and it is answered by making the
|
||||
# later lines quieter rather than by refusing to have them. Top hit full,
|
||||
# the rest as references: ~198 tokens, 1.4x the old single line, for four
|
||||
# more rules.
|
||||
# the rest as references: ~299 tokens against ~568 for five full lines, so
|
||||
# roughly 2x the old single line for four more rules.
|
||||
#
|
||||
# The reference keeps its TAIL and loses only its trigger, which is both the
|
||||
# cheaper and the safer cut — see `_rule_hint_line`, where the first attempt
|
||||
# dropped the tail as well and #3750's guard caught it within one commit.
|
||||
#
|
||||
# The consequence is deliberate and worth naming: a rule that keeps ranking
|
||||
# first for a recurring situation keeps being referenced, every time the
|
||||
@@ -1201,14 +1205,17 @@ def _rule_hint_line(rule, *, where: str, seen: bool, compact: bool = False) -> s
|
||||
like them it reads none of the others: it says how much ROOM this line
|
||||
gets, which is a fact about its rank among today's hits rather than about
|
||||
the rule. A compact line is still a full claim that the rule may apply —
|
||||
it simply cites the rule instead of quoting its trigger.
|
||||
it simply cites the rule instead of quoting its trigger. Crucially it
|
||||
still carries the `seen` tail, so the three axes stay genuinely
|
||||
independent: shortening a line must not decide what it says about whether
|
||||
the session is holding the rule.
|
||||
|
||||
WHY THE LATER LINES ARE QUIETER. Measured at #3851: a full line runs ~143
|
||||
tokens once the trigger is rendered, and #3855 tripled trigger lengths
|
||||
across the corpus, so five full lines cost ~646 tokens before every Bash
|
||||
call. Top-full-plus-references costs ~198 — 1.4x the old single line, for
|
||||
four more rules. The budget argument that once justified a single slot was
|
||||
real; what it actually forbids is four voices at full volume, not four
|
||||
across the corpus, so five full lines cost ~568 tokens before every Bash
|
||||
call. Top-full-plus-references costs ~299 — about 2x the old single line,
|
||||
for four more rules. The budget argument that once justified a single slot
|
||||
was real; what it actually forbids is four voices at full volume, not four
|
||||
voices.
|
||||
|
||||
The top hit keeps the full rendering because it is the one the ranker is
|
||||
@@ -1282,12 +1289,20 @@ def _rule_hint_line(rule, *, where: str, seen: bool, compact: bool = False) -> s
|
||||
"session's loaded set."
|
||||
)
|
||||
if compact:
|
||||
# No trigger, and a shortened tail. What survives is everything a
|
||||
# reader needs in order to DECIDE to pull it: the force (noun), the
|
||||
# identity (title) and the pointer. What goes is the trigger, which is
|
||||
# the expensive half and the half the top line already demonstrates.
|
||||
# THE TRIGGER GOES; THE TAIL STAYS. Only one of the two is expensive \u2014
|
||||
# a trigger runs 300-400 characters after #3855, the tail about 100 \u2014
|
||||
# so dropping the trigger is nearly the whole saving and dropping the
|
||||
# tail would be mostly sacrifice.
|
||||
#
|
||||
# It would also destroy the one thing #3750 exists to say. The tail is
|
||||
# what tells a reader whether they were already told this and may no
|
||||
# longer be holding it, and that is the entire difference they can act
|
||||
# on; a reference with no tail reads as a first surfacing whether it is
|
||||
# one or not. This branch shipped without it for one commit and
|
||||
# test_a_rule_the_session_already_holds_is_referenced_not_re_offered
|
||||
# caught it, which is the guard working exactly as #3750 intended.
|
||||
return (
|
||||
f"Also \u2014 {noun.lower()} \u201c{rule.title}\u201d: get_rule({rule.id})."
|
||||
f"Also \u2014 {noun.lower()} \u201c{rule.title}\u201d. {tail}"
|
||||
)
|
||||
return (
|
||||
f"{noun} that may apply {where} \u2014 \u201c{rule.title}\u201d"
|
||||
|
||||
Reference in New Issue
Block a user