0558dfed0f
Arbitration between the claimants on the utterance stream is order, hardcoded in three places, and a log that names the winner cannot explain a loss. The new package holds one record per turn: who claimed, what it would have made the turn, the score it reported, and why the rest did not get it. Being explicit that a claimant was never asked is the point: that silence is what the hardcoded ordering hides. The record rides the context, the seam querysource.go already uses, so no claim site can change a route and a context with no record costs nothing. The ring is memory and bounded: a turn record is read minutes later or never, and his words do not belong in a table that outlives the diagnosis.
50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package decision
|
|
|
|
import "sync"
|
|
|
|
// ringSize is how many turns are kept. Turns arrive at human rate, not machine
|
|
// rate, so the whole store is memory: no migration, no insert on the answer
|
|
// path, and nothing of his words survives a restart. That is what makes this
|
|
// cheap enough to leave on always (V-564). Ecosystem traces went to SQLite
|
|
// because one act writes several hops and they must outlive the turn; an
|
|
// arbitration record is read minutes later or never.
|
|
const ringSize = 25
|
|
|
|
// Ring holds the newest records, newest first on read.
|
|
type Ring struct {
|
|
mu sync.Mutex
|
|
recs []*Record
|
|
}
|
|
|
|
func NewRing() *Ring { return &Ring{} }
|
|
|
|
// Push adds one finished record and drops the oldest past the bound.
|
|
func (r *Ring) Push(rec *Record) {
|
|
if r == nil || rec == nil {
|
|
return
|
|
}
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.recs = append(r.recs, rec)
|
|
if len(r.recs) > ringSize {
|
|
r.recs = r.recs[len(r.recs)-ringSize:]
|
|
}
|
|
}
|
|
|
|
// Recent returns up to n records, newest first.
|
|
func (r *Ring) Recent(n int) []*Record {
|
|
if r == nil || n <= 0 {
|
|
return nil
|
|
}
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if n > len(r.recs) {
|
|
n = len(r.recs)
|
|
}
|
|
out := make([]*Record, 0, n)
|
|
for i := 0; i < n; i++ {
|
|
out = append(out, r.recs[len(r.recs)-1-i])
|
|
}
|
|
return out
|
|
}
|