feat(beliefs): state what a score means; gated calibration; primary uncertainty

Every breakdown now carries its meaning: an uncalibrated epistemic score
derived from recorded evidence, not a probability of truth. Belief detail
adds a calibrated estimate read from the mind's resolved predictions when
at least twenty were decided in the score's range, and otherwise says why
none is shown; and names the primary uncertainty deterministically (no live
evidence, conflicting evidence, second-hand only, unclear reading, a single
source family, little evidence). The belief list heading becomes Score.
This commit is contained in:
Andy Dixon
2026-08-09 19:14:53 +01:00
parent 08a265f0fa
commit b661496128
8 changed files with 167 additions and 3 deletions
+16
View File
@@ -16,6 +16,7 @@ import (
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/andydixon/noema/internal/beliefs"
"github.com/andydixon/noema/internal/storage"
"github.com/andydixon/noema/pkg/events"
"github.com/andydixon/noema/pkg/ids"
@@ -454,3 +455,18 @@ func (s *Store) Stats(ctx context.Context, mind ids.ID) (map[string]int64, error
}
return out, rows.Err()
}
// CalibratedScore reads a belief score against this mind's whole prediction
// history. It is the only empirical calibration Noema offers, and it says
// so when the sample is too small.
func (s *Store) CalibratedScore(ctx context.Context, mind ids.ID, score float64) beliefs.Calibrated {
c, err := s.Calibrate(ctx, mind, time.Time{})
if err != nil {
return beliefs.CalibrateWith(score, nil)
}
var buckets []beliefs.CalibrationBucket
for _, b := range c.Buckets {
buckets = append(buckets, beliefs.CalibrationBucket{Low: b.Low, High: b.High, Decided: b.Count, HitRate: b.HitRate})
}
return beliefs.CalibrateWith(score, buckets)
}