Class: Vangrail::Policy
- Inherits:
-
Struct
- Object
- Struct
- Vangrail::Policy
- Defined in:
- lib/vangrail/judgement.rb,
lib/vangrail/judgement.rb
Overview
Where the two lines are drawn between allowing, reviewing, and blocking.
Three actions rather than two, because the middle one is what a posterior makes possible and a yes-or-no rail cannot express. Most of the interesting traffic on a documentation desk lands there: one rail fired, the base rate is low, and the honest answer is that this page is a hundred times more suspicious than average and still probably fine. Blocking it costs a reader their answer; ignoring it wastes the detection. Queueing it costs somebody a minute.
The defaults are stated as what they are: a starting policy, not a finding. What they should be depends on what a false block costs against what a missed injection costs, and that is a deployment's judgement rather than a library's.
Constant Summary collapse
- DEFAULT =
Assigned outside the struct body, because a constant written inside a Struct.new block lands in the enclosing module rather than in the struct.
Policy.new(block_at: 0.5, review_at: 0.05)
Instance Attribute Summary collapse
-
#block_at ⇒ Object
Returns the value of attribute block_at.
-
#review_at ⇒ Object
Returns the value of attribute review_at.
Class Method Summary collapse
-
.from_costs(missed_attack:, false_block:, review: nil) ⇒ Object
The two lines, derived from what the three outcomes cost instead of chosen.
- .two_way(missed_attack, false_block) ⇒ Object
Instance Method Summary collapse
Instance Attribute Details
#block_at ⇒ Object
Returns the value of attribute block_at
91 92 93 |
# File 'lib/vangrail/judgement.rb', line 91 def block_at @block_at end |
#review_at ⇒ Object
Returns the value of attribute review_at
91 92 93 |
# File 'lib/vangrail/judgement.rb', line 91 def review_at @review_at end |
Class Method Details
.from_costs(missed_attack:, false_block:, review: nil) ⇒ Object
The two lines, derived from what the three outcomes cost instead of chosen.
A posterior is only half an answer: acting on it needs to know what being wrong is worth in each direction, and that is a fact about the deployment rather than about the text. Written out, the decision rule is the ordinary one from decision theory. Allowing a page costs the chance it was an attack times what a missed attack costs. Blocking costs the chance it was fine times what a wrong block costs a reader. Sending it to a person costs what a minute of their time costs, whatever the page turns out to be.
Choosing the cheapest of the three gives both thresholds directly:
reviewing beats allowing above review / missed_attack, and blocking
beats reviewing above 1 - review / false_block.
Policy.from_costs(missed_attack: 1000, false_block: 10, review: 1)
# => block above 0.9, review above 0.001
The units cancel, so they can be euros, minutes, or anything else applied consistently. What they cannot be is unstated: a threshold with no cost behind it is a preference, and this is the arithmetic that turns the preference into a claim somebody can argue with.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/vangrail/judgement.rb', line 123 def self.from_costs(missed_attack:, false_block:, review: nil) raise ArgumentError, 'costs must be positive' unless [missed_attack, false_block].all?(&:positive?) # With no human in the loop there is one line, and it is the classic # threshold: block when the expected cost of allowing exceeds the # expected cost of blocking. return two_way(missed_attack, false_block) if review.nil? raise ArgumentError, 'review cost must be positive' unless review.positive? review_at = review.fdiv(missed_attack) block_at = 1 - review.fdiv(false_block) # Reviewing everything costs more than being wrong: there is no band, and # saying so beats silently inverting the thresholds. return two_way(missed_attack, false_block) if review_at >= block_at new(block_at: block_at, review_at: review_at) end |
.two_way(missed_attack, false_block) ⇒ Object
142 143 144 145 |
# File 'lib/vangrail/judgement.rb', line 142 def self.two_way(missed_attack, false_block) threshold = false_block.fdiv(false_block + missed_attack) new(block_at: threshold, review_at: threshold) end |
Instance Method Details
#action_for(posterior) ⇒ Object
92 93 94 95 96 97 |
# File 'lib/vangrail/judgement.rb', line 92 def action_for(posterior) return :block if posterior >= block_at return :review if posterior >= review_at :allow end |