feat(watchdog): Sentry flags + quarantine on above-band Jev verdicts (#44)

This commit is contained in:
Smokey committed 2026-10-28 09:00:38 -06:00
1 parent 66cdd053f8
commit 084a4309d4
4 files changed
+198 -7

No files matched your search

+80
View File
@@ -313,6 +313,86 @@ func (f *Flock) ClassifyDivergence(ctx context.Context, state any, budgets ...*B
return p, ok
}
// watchdogQuestions is the Sentry's off-task check (E3, JEV_STRATEGIES
// strategies 4 and 8): three crisp yes/no noul questions about one crow
// message against the request it answers. noul carries no confidence, so the
// probability itself is band-thresholded (strategy 3).
var watchdogQuestions = map[string]any{
"answers_request": map[string]any{
"type": "noul",
"instructions": "Does `message` actually deliver what `request` asked of `crow`?",
"criteria": map[string]string{
"true": "It answers the request, or plainly takes it on.",
"false": "It talks around it, answers a different question, or is off-task.",
},
},
"states_fact_without_source": map[string]any{
"type": "noul",
"instructions": "Does `message` assert a checkable fact while showing no source for it?",
"criteria": map[string]string{
"true": "It states a specific, checkable fact (a number, a quote, an event) with no stated source.",
"false": "It is an opinion, a plan, or the facts it states are sourced.",
},
},
"contradicts_earlier": map[string]any{
"type": "noul",
"instructions": "Does `message` contradict what the `earlier` delivers on the same request say?",
"criteria": map[string]string{
"true": "It disagrees with an earlier deliver on the same request.",
"false": "It agrees with, or simply adds to, what `earlier` said.",
},
},
}
// ClassifyWatch asks the watchdog questions of one crow message and returns
// each answer's noul probability. ok is false on transport error or a missing
// answer (a noul answer always carries a probability).
func (j *JevClient) ClassifyWatch(ctx context.Context, state any) (map[string]float64, bool) {
if j == nil {
return nil, false
}
answers, _, _, err := j.SystemOne(ctx, state, watchdogQuestions)
if err != nil {
return nil, false
}
scores := make(map[string]float64, len(watchdogQuestions))
for name := range watchdogQuestions {
a := answers[name]
if a.Noul == nil {
return nil, false
}
scores[name] = *a.Noul
}
return scores, true
}
// ClassifyWatch runs the watchdog check under the flock guardrails (cap,
// timeout, flight log with the three noul probabilities recorded for
// calibration, strategy 5).
func (f *Flock) ClassifyWatch(ctx context.Context, state any, budgets ...*Budget) (map[string]float64, bool) {
if f.jev == nil {
return nil, false
}
for _, b := range budgets {
if err := b.Check(); err != nil {
return nil, false
}
}
release, slot := f.reserve(ctx)
if !slot {
return nil, false
}
defer release()
cctx, cancel := context.WithTimeout(ctx, f.g.CrowTimeout)
defer cancel()
start := time.Now()
scores, ok := f.jev.ClassifyWatch(cctx, state)
if f.log != nil {
f.log.Write(Event{Kind: "watch", Crow: "Sentry", Model: f.jev.model, Ms: time.Since(start).Milliseconds(), Prob: scores})
}
return scores, ok
}
// ClassifyKind tags a nest message under the same guardrails as any call:
// flock cap, timeout and flight log, with the probabilities recorded.
func (f *Flock) ClassifyKind(ctx context.Context, text string, budgets ...*Budget) (string, map[string]float64, bool) {