feat(routing): let a refusal outlive the session, and fade - #81
Merged
Merged
Conversation
A refusal was forgotten the moment the window closed. It is the clearest signal a user ever gives about routing, and it was being thrown away. It now survives, as evidence rather than as a rule. Strength halves every two weeks and is negligible by six; refusing the same context again restarts the clock and deepens the penalty, which is the escalation a browser uses for a permission prompt dismissed several times over. Once is noise, three times is an answer. Inside the session that produced it a refusal still means exactly what it meant before: that context is not raised again, full stop. Outside it, the effect is a discount on the score, so a refused context drops down the shortlist rather than off it — the same request should reach it again eventually, just not today and not without better evidence. The discount is applied after ranking rather than folded into the index, because refusals change on their own schedule and rebuilding the index for a multiplier would throw away the cache for nothing. Nothing here becomes permanent: the multiplier never reaches zero however many refusals there have been, and records are dropped once they are too old to change an outcome. The only permanent no in this plugin is manual mode, which is a decision the user made on purpose.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A refusal was forgotten the moment the window closed. It is the clearest signal a user ever gives about routing, and it was being thrown away.
What changes
A refusal now survives the session — as evidence, not as a rule.
The escalation on repeats is the one a browser uses for a permission prompt dismissed several times over: once is noise, three times is an answer.
Two deliberate limits
A refused context drops down the shortlist, never off it. The same request should reach it again eventually — just not today, and not without better evidence. Hiding it would turn a guess about the user into a fact about the store.
The multiplier never reaches zero, however many refusals there have been, and records are pruned once too old to matter. The only permanent "no" in this plugin is manual mode, because that one is a decision the user made on purpose. A guess the system made about someone should not harden into a rule.
Where it is applied
After ranking, not folded into the index. Refusals change on their own schedule, and rebuilding the index every time someone says no would throw away the cache for a multiplier. The results are re-sorted afterwards, since a discount can change the order and the shortlist's whole meaning is that it is in order.
Tests
14 new. The decay curve is pinned at each point that matters — the day it happens, two weeks (half the strength), six weeks (nothing), repeats deepening, and the floor above zero. Plus the recording path: kept beyond its session, repeats counted, works with no session to attribute it to, old records pruned from the file, and a hand-broken file still yields a usable state rather than failing the session.
The two that matter most are on a shortlist of two identically-described contexts, where nothing but the refusal can separate them: the refused one is demoted and still present, and once expired the two score equal again.
Diff coverage: 105 changed lines, 0 uncovered. Full suite 423 passing.
Scope
This is decision 6 from the design. The remaining priors — workspace binding, frecency, and switch hysteresis — are the last piece and are their own change.