返回 DeepSeek-Reasonix
runway_shadow.go
根目录 / internal / evidence / runway_shadow.go
1 package evidence
2
3 // The runway shadow prices one turn's investigation without changing any
4 // runtime decision. Every round costs the same; observable outcomes buy some
5 // or all of that cost back. Keeping the account private to OutcomeTracker makes
6 // the experiment telemetry-only until recorded data justifies a policy change.
7 const (
8 runwayRoundCost = 4
9 runwayYieldFalsifiable = 3 * runwayRoundCost
10 runwayYieldChange = runwayRoundCost
11 runwayYieldExploration = runwayRoundCost - 1
12
13 // In exploration-rate units, a fresh account covers 24 productive reads and
14 // a fully banked one covers 40. A round producing nothing burns four units.
15 runwayStartBalance = 24
16 runwayMaxBalance = 40
17 )
18
19 type runwayShadow struct {
20 balance int
21 observed bool
22 dry int
23 idle int
24 }
25
26 type runwayShadowState struct {
27 balance int
28 dry int
29 idle int
30 spent bool
31 }
32
33 func (r *runwayShadow) observe(s OutcomeSample) runwayShadowState {
34 if !r.observed {
35 r.balance, r.observed = runwayStartBalance, true
36 }
37 yield := runwayYield(s)
38 wasSolvent := r.balance > 0
39 r.balance = min(max(r.balance+yield-runwayRoundCost, 0), runwayMaxBalance)
40
41 if s.Discriminating > 0 || s.Objective > 0 || s.Churn > 0 {
42 r.idle = 0
43 } else {
44 r.idle++
45 }
46 if yield > 0 {
47 r.dry = 0
48 } else {
49 r.dry++
50 }
51 return runwayShadowState{
52 balance: r.balance,
53 dry: r.dry,
54 idle: r.idle,
55 spent: wasSolvent && r.balance == 0,
56 }
57 }
58
59 func runwayYield(s OutcomeSample) int {
60 switch {
61 case s.Discriminating > 0 || s.Objective > 0:
62 return runwayYieldFalsifiable
63 case s.Churn > 0:
64 return runwayYieldChange
65 case s.Exploration > 0:
66 return runwayYieldExploration
67 default:
68 return 0
69 }
70 }
71
71 lines GO