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
+72
View File
@@ -0,0 +1,72 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$d := .Data.Detail}}{{$b := $d.Belief}}{{$bd := $d.Breakdown}}
<div class="pagehead">
<p class="muted small"><a href="/minds/{{$m.Slug}}">{{$m.DisplayName}}</a> › <a href="/minds/{{$m.Slug}}/beliefs">beliefs</a></p>
<h1><span class="badge st-{{$b.Status}}">{{$b.Status}}</span> {{$b.Proposition}}</h1>
<p class="muted">{{$b.Epistemic}} · version {{$b.Version}} · first {{date $b.FirstCreated}} · evaluated {{$b.Evaluations}}× · source {{$b.Source}}{{if $b.Subject}} · subject <span class="mono">{{$b.Subject}}</span>{{end}}{{if $b.PinnedStatus}} · <span class="badge warn">status pinned by operator</span>{{end}}</p>
</div>
<section class="grid cols-3-1">
<div class="stack">
<div class="card">
<h2>Confidence {{conf $b.Confidence}} <span class="muted small">how it was computed</span></h2>
<div class="conf big"><span class="conf-bar wide"><i style="width:{{pct $b.Confidence}}"></i></span></div>
<dl class="kv small">
<dt>Prior</dt><dd>{{conf $bd.Prior}} <span class="muted">({{$b.Epistemic}})</span></dd>
<dt>Support</dt><dd>{{conf $bd.For}} <span class="muted">Σ weight × credibility over {{$bd.SourcesFor}} source(s); diversity scale {{conf $bd.DiversityScale}}</span></dd>
<dt>Opposition</dt><dd>{{conf $bd.Against}}</dd>
<dt>Log-odds</dt><dd>{{conf $bd.LogOdds}}</dd>
<dt>Formula</dt><dd class="mono">{{$bd.Formula}}</dd>
{{range $bd.Notes}}<dt>Note</dt><dd class="warn">{{.}}</dd>{{end}}
</dl>
</div>
<div class="grid cols-2">
<div class="card">
<h2>Evidence for</h2>
<ul class="plain ev">{{range $d.Evidence}}{{if eq .Direction "for"}}<li class="{{if .Retracted}}retracted{{end}}"><div><strong>{{conf .Weight}}</strong> × cred {{conf .Credibility}} <span class="badge">{{.Kind}}</span> {{if .Retracted}}<span class="badge danger">retracted</span>{{end}}</div><div>{{.Note}}</div><div class="muted small">{{.Source}} · {{ago .CreatedAt}} {{if and (eq .Kind "event") .RefID}}· <a href="/events/{{.RefID}}">event</a>{{else if and (eq .Kind "memory") .RefID}}· <a href="/minds/{{$m.Slug}}/memory/{{.RefID}}">memory</a>{{else if and (eq .Kind "belief") .RefID}}· <a href="/minds/{{$m.Slug}}/beliefs/{{.RefID}}">belief</a>{{end}}</div></li>{{end}}{{else}}<li class="muted">None.</li>{{end}}</ul>
</div>
<div class="card">
<h2>Evidence against</h2>
<ul class="plain ev">{{range $d.Evidence}}{{if eq .Direction "against"}}<li class="{{if .Retracted}}retracted{{end}}"><div><strong>{{conf .Weight}}</strong> × cred {{conf .Credibility}} <span class="badge">{{.Kind}}</span></div><div>{{.Note}}</div><div class="muted small">{{.Source}} · {{ago .CreatedAt}} {{if and (eq .Kind "belief") .RefID}}· <a href="/minds/{{$m.Slug}}/beliefs/{{.RefID}}">belief</a>{{else if and (eq .Kind "event") .RefID}}· <a href="/events/{{.RefID}}">event</a>{{end}}</div></li>{{end}}{{else}}<li class="muted">None.</li>{{end}}</ul>
</div>
</div>
<div class="card">
<h2>Alternative explanations</h2>
{{if $d.Alternatives}}<ul class="plain">{{range $d.Alternatives}}<li>{{.Proposition}} <span class="muted small">({{.Status}}{{if .Note}} · {{.Note}}{{end}})</span></li>{{end}}</ul>{{else}}<p class="muted small">None recorded.</p>{{end}}
{{if .Principal.User.Role.AtLeast "operator"}}<form method="post" action="/minds/{{$m.Slug}}/beliefs/{{$b.ID}}/alternatives" class="row"><input type="hidden" name="_csrf" value="{{.CSRF}}"><input name="proposition" placeholder="another explanation" size="40" required><button class="btn small" type="submit">Add</button></form>{{end}}
</div>
{{if $d.Links}}<div class="card"><h2>Contradictions and links</h2><ul class="plain">{{range $d.Links}}<li><span class="badge danger">{{.Relation}}</span> <a href="/minds/{{$m.Slug}}/beliefs/{{.Other.ID}}">{{.Other.Proposition}}</a> <span class="muted small">({{.Other.Status}}, {{conf .Other.Confidence}})</span></li>{{end}}</ul></div>{{end}}
<div class="card">
<h2>History <span class="muted small">append-only</span></h2>
<table class="table compact"><thead><tr><th>v</th><th>Status</th><th class="num">Conf</th><th>Reason</th><th>When</th><th></th></tr></thead>
<tbody>{{range $d.Versions}}<tr><td>{{.Version}}</td><td><span class="badge st-{{.Status}}">{{.Status}}</span></td><td class="num">{{conf .Confidence}}</td><td class="small">{{.Reason}}</td><td class="small muted nowrap">{{.CreatedAt.Local.Format "01-02 15:04:05"}}</td><td>{{if .EventID}}<a class="small" href="/events/{{.EventID}}">event</a>{{end}}</td></tr>{{end}}</tbody></table>
</div>
</div>
<div class="stack">
{{if .Principal.User.Role.AtLeast "operator"}}
<div class="card">
<h2>Add evidence</h2>
<form method="post" action="/minds/{{$m.Slug}}/beliefs/{{$b.ID}}/evidence" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Direction <select name="direction"><option value="for">for</option><option value="against">against</option></select></label>
<label>Note <input name="note" required maxlength="500" placeholder="what you observed"></label>
<label class="slider"><span>Weight</span><input type="range" name="weight" min="0.1" max="1" step="0.1" value="0.5" data-out><output>0.5</output></label>
<div><button class="btn small" type="submit">Add</button></div>
</form>
</div>
{{if ne $b.Status "rejected"}}
<div class="card">
<h2>Correct</h2>
<form method="post" action="/minds/{{$m.Slug}}/beliefs/{{$b.ID}}/retract" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Why it is wrong <input name="reason" required maxlength="500"></label>
<label class="check"><input type="checkbox" name="do_not_infer" value="1"> Do not infer this again</label>
<div><button class="btn btn-danger" type="submit" data-confirm="Mark this belief wrong? History is kept.">This belief is wrong</button></div>
</form>
</div>
{{end}}
{{end}}
<div class="card"><h2>Events</h2>{{if $d.Origin}}<p class="small">Origin: <a href="/events/{{$d.Origin.ID}}">{{$d.Origin.Summary}}</a></p>{{end}}{{if $d.Events}}<ul class="plain small">{{range $d.Events}}<li><span class="etype cat-{{.Category}}">{{.Type}}</span> <a href="/events/{{.ID}}">{{.Summary}}</a></li>{{end}}</ul>{{else}}<p class="muted small">None.</p>{{end}}</div>
<div class="card small"><span class="muted">ID</span> <span class="mono">{{$b.ID}}</span></div>
</div>
</section>
{{end}}
+57
View File
@@ -0,0 +1,57 @@
{{define "content"}}
{{$m := .Data.Mind}}
<div class="pagehead mindhead">
<div><h1>{{$m.DisplayName}} <span class="muted small">beliefs</span></h1><p class="muted">{{.Data.Stats.Total}} beliefs · {{range $k, $v := .Data.Stats.ByStatus}}<span class="badge st-{{$k}}">{{$k}} {{$v}}</span> {{end}}</p></div>
</div>
{{template "mind_tabs" (dict "M" $m "Active" "beliefs")}}
<form method="get" class="card filterbar">
<label>Search <input name="q" value="{{index .Data.Q "q"}}" size="24" placeholder="proposition contains"></label>
<label>Status <select name="status"><option value="">all live</option>{{range beliefStatuses}}<option value="{{.}}" {{if eq (index $.Data.Q "status") (print .)}}selected{{end}}>{{.}}</option>{{end}}</select></label>
<label>Min confidence <input type="number" name="min_confidence" min="0" max="1" step="0.1" value="{{index .Data.Q "min_confidence"}}" size="4"></label>
<label>Max confidence <input type="number" name="max_confidence" min="0" max="1" step="0.1" value="{{index .Data.Q "max_confidence"}}" size="4"></label>
<button class="btn" type="submit">Filter</button> <a class="btn btn-ghost" href="/minds/{{$m.Slug}}/beliefs">Reset</a>
</form>
<section class="grid cols-3-1">
<div class="card">
{{if .Data.Beliefs}}
<table class="table compact">
<thead><tr><th>Status</th><th>Proposition</th><th class="num">Confidence</th><th>Kind</th><th>Evaluated</th></tr></thead>
<tbody>{{range .Data.Beliefs}}<tr>
<td><span class="badge st-{{.Status}}">{{.Status}}</span></td>
<td><a href="/minds/{{$m.Slug}}/beliefs/{{.ID}}">{{.Proposition}}</a>{{if .Subject}} <span class="muted small mono">{{.Subject}}</span>{{end}}</td>
<td class="num"><span class="conf"><span class="conf-bar"><i style="width:{{pct .Confidence}}"></i></span> {{conf .Confidence}}</span></td>
<td class="small muted">{{.Epistemic}} · v{{.Version}}</td>
<td class="small muted nowrap">{{ago .LastEvaluated}}</td>
</tr>{{end}}</tbody>
</table>
{{else}}<p class="muted">No beliefs yet. Hypotheses form from co-occurring observations; operators can assert knowledge on the right.</p>{{end}}
</div>
<div class="stack">
{{if .Principal.User.Role.AtLeast "operator"}}
<div class="card">
<h2>Assert a belief <span class="muted small">your statement is the evidence</span></h2>
<form method="post" action="/minds/{{$m.Slug}}/beliefs" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Proposition <textarea name="proposition" rows="2" required maxlength="2000"></textarea></label>
<div class="grid cols-2 tight">
<label>Subject <input name="subject" placeholder="server-01" maxlength="80"></label>
<label>Epistemic <select name="epistemic">{{range epistemicKinds}}<option value="{{.}}" {{if eq . "claim"}}selected{{end}}>{{.}}</option>{{end}}</select></label>
</div>
<label class="slider"><span>How firmly</span><input type="range" name="weight" min="0.1" max="1" step="0.1" value="0.7" data-out><output>0.7</output></label>
<div><button class="btn" type="submit">Assert</button></div>
</form>
</div>
<div class="card">
<h2>Do not infer</h2>
<form method="post" action="/minds/{{$m.Slug}}/corrections" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Statement the mind must not conclude <textarea name="statement" rows="2" required maxlength="2000"></textarea></label>
<label>Reason <input name="reason" maxlength="500"></label>
<div><button class="btn" type="submit">Forbid inference</button></div>
</form>
</div>
{{end}}
{{if .Data.Corrections}}<div class="card"><h2>Corrections <span class="muted small">{{len .Data.Corrections}}</span></h2><ul class="plain small">{{range .Data.Corrections}}<li><span class="badge">{{.Kind}}</span> {{.Statement}}<div class="muted">{{.Reason}} · {{.CreatedBy}} · {{ago .CreatedAt}}</div></li>{{end}}</ul></div>{{end}}
</div>
</section>
{{end}}
+7
View File
@@ -42,6 +42,13 @@
</dl>
</div>
</section>
<section class="grid cols-2">
<div class="card">
<h2>Important beliefs <span class="muted small"><a href="/minds/{{$m.Slug}}/beliefs">{{.Data.BeliefStats.Total}} total</a></span></h2>
{{if .Data.Beliefs}}<table class="table compact"><tbody>{{range .Data.Beliefs}}<tr><td><span class="badge st-{{.Status}}">{{.Status}}</span></td><td><a href="/minds/{{$m.Slug}}/beliefs/{{.ID}}">{{.Proposition}}</a></td><td class="num"><span class="conf"><span class="conf-bar"><i style="width:{{pct .Confidence}}"></i></span> {{conf .Confidence}}</span></td></tr>{{end}}</tbody></table>
{{else}}<p class="muted small">No beliefs yet.</p>{{end}}
</div>
</section>
<section class="grid cols-2">
<div class="card">
<h2>Personality brief <span class="muted small">what cognitive prompts receive</span></h2>
+1 -1
View File
@@ -3,6 +3,6 @@
<a href="/minds/{{$m.Slug}}" {{if eq $a "overview"}}aria-current="page"{{end}}>Overview</a>
<a href="/minds/{{$m.Slug}}/thoughts" {{if eq $a "thoughts"}}aria-current="page"{{end}}>Thoughts</a>
<a href="/minds/{{$m.Slug}}/personality" {{if eq $a "personality"}}aria-current="page"{{end}}>Personality</a>
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a class="disabled" title="Phase 6">Beliefs</a><a class="disabled" title="Phase 8">Conversation</a>
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a class="disabled" title="Phase 8">Conversation</a>
</nav>
{{end}}