feat: add beliefs with evidence-derived confidence, contradictions and corrections

Phase 6. Beliefs are propositions with a prior set by epistemic kind and a
confidence computed from typed evidence: sigmoid(logit(prior) + slope ×
(support × diversity − opposition)), where support from a single source
is discounted so repetition cannot manufacture certainty, and bounds keep
the mind away from absolute certainty. Every evaluation stores its
breakdown and appends a version row protected by the append-only
trigger; the belief page shows the arithmetic, evidence for and against,
alternatives, contradictions and history.

Contradiction detection is deterministic first (negation plus token
overlap): a hit emits contradiction_detected, links both beliefs and
makes each counter-evidence for the other so both trend to contested.
Operators can assert beliefs (their statement is the evidence), add
evidence, mark a belief wrong (pinned rejected, history kept) and forbid
an inference; do-not-infer corrections block matching hypotheses.
Retracting a memory retracts the evidence that cited it and re-evaluates
the beliefs concerned.

The associate process proposes low-confidence causal hypotheses from
co-occurring notable observations, and the hypothesis handler turns
admitted ones into beliefs with automatic alternatives, closing demo
scenarios 1 and 2 without any model call.
This commit is contained in:
Andy Dixon
2026-06-21 22:14:12 +01:00
parent 03631e5685
commit 73e5753a91
29 changed files with 2344 additions and 20 deletions
+78
View File
@@ -0,0 +1,78 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"strings"
)
func init() {
extraCommands = append(extraCommands, command{"belief", "list --mind m [-status s]|inspect <mind> <id>|assert <mind> <text>|wrong <mind> <id> <reason> – beliefs", cmdBelief})
}
func cmdBelief(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "usage: noemactl belief list --mind <mind> [-status s] [-json] | inspect <mind> <id> | assert <mind> <text...> | wrong <mind> <id> <reason...>")
return exitUsage
}
switch args[0] {
case "list":
fs := flag.NewFlagSet("belief list", flag.ContinueOnError)
mind := fs.String("mind", "", "mind slug or id")
status := fs.String("status", "", "status filter")
asJSON := fs.Bool("json", false, "raw JSON")
if fs.Parse(args[1:]) != nil || *mind == "" {
fmt.Fprintln(os.Stderr, "usage: noemactl belief list --mind <mind> [-status s]")
return exitUsage
}
var out struct{ Items []map[string]any }
if code, err := apiRequest("GET", "/api/v1/minds/"+*mind+"/beliefs?status="+*status, nil, &out); err != nil {
return exitFor(code, err)
}
if *asJSON {
json.NewEncoder(os.Stdout).Encode(out.Items) //nolint:errcheck
return exitOK
}
for _, b := range out.Items {
fmt.Printf("%.2f %-10v %s (%v)\n", num(b["confidence"]), b["status"], b["proposition"], b["id"])
}
return exitOK
case "inspect":
if len(args) < 3 {
return exitUsage
}
var d map[string]any
if code, err := apiRequest("GET", "/api/v1/minds/"+args[1]+"/beliefs/"+args[2], nil, &d); err != nil {
return exitFor(code, err)
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(d) //nolint:errcheck
return exitOK
case "assert":
if len(args) < 3 {
return exitUsage
}
var res map[string]any
if code, err := apiRequest("POST", "/api/v1/minds/"+args[1]+"/beliefs", map[string]any{"proposition": strings.Join(args[2:], " ")}, &res); err != nil {
return exitFor(code, err)
}
b, _ := res["Belief"].(map[string]any)
fmt.Printf("recorded %v belief %v with confidence %.2f\n", b["status"], b["id"], num(b["confidence"]))
return exitOK
case "wrong":
if len(args) < 4 {
return exitUsage
}
var res map[string]any
if code, err := apiRequest("POST", "/api/v1/minds/"+args[1]+"/beliefs/"+args[2]+"/retract", map[string]any{"reason": strings.Join(args[3:], " ")}, &res); err != nil {
return exitFor(code, err)
}
fmt.Println("belief rejected; history preserved")
return exitOK
}
fmt.Fprintln(os.Stderr, "unknown belief subcommand", args[0])
return exitUsage
}