diff --git a/internal/claim/claim.go b/internal/claim/claim.go index c704b4f..6cd8fb9 100644 --- a/internal/claim/claim.go +++ b/internal/claim/claim.go @@ -2,8 +2,8 @@ // utterance (V-565, umbrella V-558, design in // docs/plans/19-dialogue-arbitration.md). // -// Maven's cascade has roughly ten stage-0 grammars, seven router intents, -// twenty-two query sources and four stateful pre-emptors, and every one of them +// Maven's cascade has twenty-two stage-0 grammars, seven router intents, +// twenty-two query sources and seven stateful pre-emptors, and every one of them // answers "is this mine?" alone. None can answer "is this more mine than // yours?", because their scores are not comparable: stage 0 asserts 1.0 by // fiat, the classifier reports a cosine, the LLM router derives one from @@ -56,8 +56,9 @@ const ( // BandStructural — the claimant read the whole sentence and produced a // complete route, every slot its intent requires filled. The LLM router at // full confidence, and a stateful claimant holding a pending question. - // Below BandAnchored on purpose: the four stateful claimants pre-empt - // unconditionally today, and that is the V-558 defect. + // Below BandAnchored on purpose: the stateful claimants pre-empt + // unconditionally today, and that is the V-558 defect. There are seven of + // them and preRouteLadder in cmd/mavend/decisiontrace.go is the roster. BandStructural // BandAnchored — a literal pattern anchored in the utterance matched, and diff --git a/internal/decision/ring.go b/internal/decision/ring.go index d1a3b64..d0db693 100644 --- a/internal/decision/ring.go +++ b/internal/decision/ring.go @@ -19,6 +19,13 @@ type Ring struct { func NewRing() *Ring { return &Ring{} } // Push adds one finished record and drops the oldest past the bound. +// +// The dropped pointers are cleared before the reslice. Resliceing alone moves +// the window forward and leaves the evicted records addressable from the +// backing array, so up to ringSize turns he had already aged out stayed in +// memory until the next append reallocated. That is a leak anywhere and it is +// the wrong one here, because the reason this store is memory-only is that his +// words should not outlive the diagnosis. func (r *Ring) Push(rec *Record) { if r == nil || rec == nil { return @@ -26,8 +33,11 @@ func (r *Ring) Push(rec *Record) { 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:] + if drop := len(r.recs) - ringSize; drop > 0 { + for i := 0; i < drop; i++ { + r.recs[i] = nil + } + r.recs = r.recs[drop:] } } diff --git a/internal/netscan/netscan.go b/internal/netscan/netscan.go index b2e28fa..4c8af56 100644 --- a/internal/netscan/netscan.go +++ b/internal/netscan/netscan.go @@ -33,6 +33,7 @@ import ( "net/netip" "os" "sort" + "strconv" "strings" "sync" "time" @@ -323,7 +324,7 @@ scan: go func(addr string, port int) { defer wg.Done() defer func() { <-sem }() - if s.dial(ctx, net.JoinHostPort(addr, itoa(port)), s.cfg.Timeout) { + if s.dial(ctx, net.JoinHostPort(addr, strconv.Itoa(port)), s.cfg.Timeout) { results <- result{addr: addr, ports: []int{port}} } }(addr, port) @@ -371,8 +372,6 @@ scan: return Result{Hosts: out, Truncated: truncated}, nil } -func itoa(n int) string { return fmt.Sprintf("%d", n) } - func dialTCP(ctx context.Context, addr string, timeout time.Duration) bool { d := net.Dialer{Timeout: timeout} ctx, cancel := context.WithTimeout(ctx, timeout)