feat(ui): wants, intentions, commitments, plans, agency timeline, why, research

- Wants: active wants by effective strength with origin, means, drives,
  values, goal, reinforcement, suppressions and each conflict with its
  resolution rule; closed wants with their resolution.
- Intentions grouped by status with blocked reasons and plans, plus the
  wants that did not become intentions and why. An intention page shows
  sources, subordinate intentions, formation terms, every plan revision as
  a dependency graph, decisions and the correlated timeline; operators can
  abandon it.
- Commitments with polarity, exception, matcher, weight and due time,
  live conflicts between commitments, a creation form and close controls.
- Plans with estimate against budget; a plan page with the dependency
  graph, per-step expectations, observations and linked approvals,
  requests, executions and subordinate intentions, resources, completion
  and abort conditions, assumptions, alternatives, revisions and
  validation; operators can cancel a plan or release a blocked step.
- Agency timeline labelling each event as wanted, intended, committed,
  planned, requested, approved, acted, observed or reconsidered.
- "Why did this happen?" pages built from agency.Explain, reachable from
  events, executions in the approval centre, plan steps and intentions.
- A research dashboard per mind (volition and agency rates, introspection
  accuracy, action claims, self-model accuracy, calibration by confidence
  band, experiments on the mind) and a global research page grouping
  experiments by protocol; every figure sits next to its operational
  definition and the disclaimer. Experiment pages show the protocol,
  reproducibility, build, per-variant research measures, divergence
  extras and counterfactual findings.
- The mind tabs gain Volition; the navigation gains Research.
This commit is contained in:
Andy Dixon
2026-09-24 21:16:48 +01:00
parent a43fc31643
commit 2ecdbc39b5
23 changed files with 975 additions and 5 deletions
+7 -1
View File
@@ -116,7 +116,13 @@ func (u *UI) approvalsPage(w http.ResponseWriter, r *http.Request) {
for _, c := range u.deps.Capabilities.Registry.List() {
caps[c.Name] = c
}
u.render(w, r, "approvals", "Approval centre", map[string]any{"Pending": pending, "Decided": decided, "Executions": execs, "MindNames": names, "Batches": batches, "Grants": grants, "Caps": caps})
slugs := map[ids.ID]string{}
if all, err := u.deps.Minds.List(r.Context(), true); err == nil {
for _, m := range all {
slugs[m.ID] = m.Slug
}
}
u.render(w, r, "approvals", "Approval centre", map[string]any{"Pending": pending, "Decided": decided, "Executions": execs, "MindNames": names, "Batches": batches, "Grants": grants, "Caps": caps, "MindSlugs": slugs})
}
// requestBatch groups requests raised for one intention (or one mind in one
+46
View File
@@ -0,0 +1,46 @@
package ui
import (
"bytes"
"encoding/json"
"log/slog"
"strings"
"testing"
"github.com/andydixon/noema/internal/app"
"github.com/andydixon/noema/internal/auth"
"github.com/andydixon/noema/internal/config"
"github.com/andydixon/noema/internal/experiments"
"github.com/andydixon/noema/internal/minds"
"github.com/andydixon/noema/internal/snapshots"
"github.com/andydixon/noema/pkg/ids"
)
// The experiment page renders protocol, reproducibility, research measures
// and counterfactual findings without template errors.
func TestExperimentTemplateRendersResearchResults(t *testing.T) {
u, err := New(app.Deps{Config: config.Defaults(), Log: slog.New(slog.DiscardHandler)}, Actions{})
if err != nil {
t.Fatal(err)
}
acc := 0.5
res := experiments.Results{Variants: []experiments.VariantResult{
{Name: "admitted", Wants: []string{"a"}, Intentions: []string{}, Downstream: map[string]int{"memory": 2}, IntrospectionAccuracy: &acc, SelfModelAccuracy: &acc, CorrectionLatency: 1, LLMCalls: map[string]int{}, EventsByType: map[string]int{}},
{Name: "excluded", Ablations: []string{"metacognition"}, CorrectionLatency: -1, LLMCalls: map[string]int{}, EventsByType: map[string]int{}},
}, Comparison: &experiments.Comparison{FirstDivergentStimulus: 0, DownstreamDelta: map[string]int{"memory": 2}, ArchitectureShare: 0.4},
Reproducibility: experiments.Reproducibility{Status: "single run", Digest: "abc"},
Counterfactual: &experiments.CounterfactualReport{OmittedSummaries: []string{"disk errors"}, Findings: []string{"the belief X was never formed"}}}
raw, _ := json.Marshal(res)
e := experiments.Experiment{ID: ids.New(), Name: "gw", Hypothesis: "admission matters", Status: "done", Results: raw,
Definition: experiments.Definition{Protocol: &experiments.Protocol{Kind: "global_workspace", IndependentVariable: "admission", ControlledVariables: []string{"stimulus"}, Measures: []string{"workspace.downstream_events"}}}}
var buf bytes.Buffer
err = u.pages["experiment"].ExecuteTemplate(&buf, "layout", View{Principal: &auth.Principal{User: auth.User{Username: "watcher", Role: auth.RoleObserver}}, Data: map[string]any{"E": e, "Results": experiments.ResultsOf(e), "Snapshot": snapshots.Snapshot{}, "Base": minds.Mind{}, "Subject": nil, "DefinitionJSON": "{}"}})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"Independent variable", "single run", "Counterfactual", "the belief X was never formed", "Architecture share", "metacognition"} {
if !strings.Contains(buf.String(), want) {
t.Errorf("missing %q", want)
}
}
}
+1
View File
@@ -396,6 +396,7 @@ func (u *UI) Register(m *http.ServeMux) {
u.dreamRoutes(m)
u.studyRoutes(m)
u.privacyRoutes(m)
u.volitionRoutes(m)
for _, p := range []struct{ path, title, note string }{} {
m.Handle("GET "+p.path, u.require(auth.RoleObserver, func(w http.ResponseWriter, r *http.Request) { u.render(w, r, "placeholder", p.title, p.note) }))
+548
View File
@@ -0,0 +1,548 @@
package ui
import (
"encoding/json"
"net/http"
"sort"
"strconv"
"strings"
"time"
"github.com/andydixon/noema/internal/agency"
"github.com/andydixon/noema/internal/auth"
evstore "github.com/andydixon/noema/internal/events"
"github.com/andydixon/noema/internal/experiments"
"github.com/andydixon/noema/internal/planning"
"github.com/andydixon/noema/internal/research"
"github.com/andydixon/noema/internal/volition"
"github.com/andydixon/noema/pkg/events"
"github.com/andydixon/noema/pkg/ids"
)
func (u *UI) volitionRoutes(m *http.ServeMux) {
m.Handle("GET /minds/{mind}/wants", u.require(auth.RoleObserver, u.wantsPage))
m.Handle("GET /minds/{mind}/intentions", u.require(auth.RoleObserver, u.intentionsPage))
m.Handle("GET /minds/{mind}/intentions/{id}", u.require(auth.RoleObserver, u.intentionPage))
m.Handle("POST /minds/{mind}/intentions/{id}/abandon", u.require(auth.RoleOperator, u.intentionAbandon))
m.Handle("GET /intentions/{id}", u.require(auth.RoleObserver, u.intentionRedirect))
m.Handle("GET /minds/{mind}/commitments", u.require(auth.RoleObserver, u.commitmentsPage))
m.Handle("POST /minds/{mind}/commitments", u.require(auth.RoleOperator, u.commitmentCreate))
m.Handle("POST /minds/{mind}/commitments/{id}/close", u.require(auth.RoleOperator, u.commitmentClose))
m.Handle("GET /minds/{mind}/plans", u.require(auth.RoleObserver, u.plansPage))
m.Handle("GET /minds/{mind}/plans/{id}", u.require(auth.RoleObserver, u.planPage))
m.Handle("POST /minds/{mind}/plans/{id}/cancel", u.require(auth.RoleOperator, u.planCancel))
m.Handle("POST /minds/{mind}/plans/{id}/steps/{step}/retry", u.require(auth.RoleOperator, u.planStepRetry))
m.Handle("GET /minds/{mind}/agency", u.require(auth.RoleObserver, u.agencyPage))
m.Handle("GET /minds/{mind}/why", u.require(auth.RoleObserver, u.whyPage))
m.Handle("GET /minds/{mind}/research", u.require(auth.RoleObserver, u.researchPage))
m.Handle("GET /research", u.require(auth.RoleObserver, u.researchIndex))
}
type wantRow struct {
volition.Want
Eff float64
Conflicts []conflictView
}
type conflictView struct {
Other, Kind, Outcome, Rule string
Preferred bool
At time.Time
}
func (u *UI) wantsPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
ctx := r.Context()
all, _ := u.deps.Volition.Wants(ctx, nil, m.ID, nil, 500)
byID := map[ids.ID]volition.Want{}
for _, x := range all {
byID[x.ID] = x
}
conflicts, _ := u.deps.Volition.Conflicts(ctx, m.ID, "", 500)
now := time.Now()
var active, closed []wantRow
for _, x := range all {
row := wantRow{Want: x, Eff: x.Effective(now)}
for _, c := range conflicts {
other := ids.Nil
switch x.ID {
case c.WantA:
other = c.WantB
case c.WantB:
other = c.WantA
default:
continue
}
rule, _ := c.Rationale["rule"].(string)
row.Conflicts = append(row.Conflicts, conflictView{Other: byID[other].Description, Kind: c.Kind, Outcome: c.Resolution + " (" + c.Status + ")", Rule: rule, Preferred: c.Preferred == x.ID, At: c.CreatedAt})
}
if x.Status == volition.WantActive {
active = append(active, row)
} else {
closed = append(closed, row)
}
}
sort.SliceStable(active, func(i, j int) bool { return active[i].Eff > active[j].Eff })
goalTitles := map[ids.ID]string{}
if gs, err := u.deps.Goals.List(ctx, m.ID, nil, 300); err == nil {
for _, g := range gs {
goalTitles[g.ID] = g.Title
}
}
u.render(w, r, "wants", m.DisplayName+" · Wants", map[string]any{"Mind": m, "Active": active, "Closed": closed, "Goals": goalTitles, "HalfLife": volition.WantHalfLife.String(), "Threshold": volition.WantThreshold})
}
func (u *UI) intentionsPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
all, _ := u.deps.Volition.Intentions(r.Context(), nil, m.ID, nil, 500)
groups := map[string][]volition.Intention{}
for _, it := range all {
groups[string(it.Status)] = append(groups[string(it.Status)], it)
}
order := []string{"active", "blocked", "suspended", "proposed", "fulfilled", "abandoned", "superseded"}
notFormed, _ := u.deps.Volition.Decisions(r.Context(), m.ID, volition.DecisionFilter{Kind: "non_formation", Limit: 30})
u.render(w, r, "intentions", m.DisplayName+" · Intentions", map[string]any{"Mind": m, "Groups": groups, "Order": order, "NotFormed": notFormed})
}
func (u *UI) intentionRedirect(w http.ResponseWriter, r *http.Request) {
id, err := ids.Parse(r.PathValue("id"))
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such intention.")
return
}
it, err := u.deps.Volition.GetIntentionAny(r.Context(), id)
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such intention.")
return
}
m, err := u.deps.Minds.Get(r.Context(), it.MindID)
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such mind.")
return
}
http.Redirect(w, r, "/minds/"+m.Slug+"/intentions/"+string(id), http.StatusSeeOther)
}
// dagLevel is one column of a plan's dependency graph.
type dagLevel struct {
Level int
Steps []planning.Step
}
func planLevels(p planning.Plan) []dagLevel {
level := map[string]int{}
byKey := map[string]planning.Step{}
for _, s := range p.Steps {
byKey[s.Key] = s
}
var depth func(k string, seen map[string]bool) int
depth = func(k string, seen map[string]bool) int {
if l, ok := level[k]; ok {
return l
}
if seen[k] {
return 0
}
seen[k] = true
l := 0
for _, d := range byKey[k].DependsOn {
if x := depth(d, seen) + 1; x > l {
l = x
}
}
level[k] = l
return l
}
maxL := 0
for _, s := range p.Steps {
if l := depth(s.Key, map[string]bool{}); l > maxL {
maxL = l
}
}
out := make([]dagLevel, maxL+1)
for i := range out {
out[i].Level = i
}
for _, s := range p.Steps {
out[level[s.Key]].Steps = append(out[level[s.Key]].Steps, s)
}
return out
}
func (u *UI) intentionPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, err := ids.Parse(r.PathValue("id"))
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such intention.")
return
}
d, err := u.studyF().IntentionDetail(r, m.ID, id)
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such intention.")
return
}
formation, _ := json.MarshalIndent(d.Intention.Formation, "", " ")
var levels [][]dagLevel
for _, p := range d.Plans {
levels = append(levels, planLevels(p))
}
timeline, _ := u.studyF().AgencyTimeline(r, m.ID, 200, id)
u.render(w, r, "intention", m.DisplayName+" · Intention", map[string]any{"Mind": m, "D": d, "Formation": string(formation), "Levels": levels, "Timeline": timeline})
}
func (u *UI) intentionAbandon(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, _ := ids.Parse(r.PathValue("id"))
r.ParseForm() //nolint:errcheck
to := "/minds/" + m.Slug + "/intentions/" + string(id)
if _, err := u.studyF().AbandonIntention(r, m.ID, id, r.PostFormValue("reason")); err != nil {
u.flash(w, r, "error", err.Error(), to)
return
}
u.flash(w, r, "ok", "Intention abandoned; its plan was cancelled and open requests withdrawn.", to)
}
func (u *UI) commitmentsPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
all, _ := u.deps.Volition.Commitments(r.Context(), nil, m.ID, nil, 500)
var active, closed []volition.Commitment
for _, c := range all {
if c.Status == "active" {
active = append(active, c)
} else {
closed = append(closed, c)
}
}
conflicts := volition.CommitmentConflicts(active, time.Now())
u.render(w, r, "commitments", m.DisplayName+" · Commitments", map[string]any{"Mind": m, "Active": active, "Closed": closed, "Conflicts": conflicts,
"Kinds": []string{"operator_instruction", "promise", "responsibility", "value_rule", "relationship", "prior_decision", "policy", "scheduled"}})
}
func (u *UI) commitmentCreate(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
r.ParseForm() //nolint:errcheck
to := "/minds/" + m.Slug + "/commitments"
split := func(k string) []string {
var out []string
for _, x := range strings.Split(r.PostFormValue(k), ",") {
if x = strings.TrimSpace(x); x != "" {
out = append(out, x)
}
}
return out
}
n := volition.NewCommitment{MindID: m.ID, Description: r.PostFormValue("description"), Kind: volition.CommitmentKind(r.PostFormValue("kind")), Polarity: r.PostFormValue("polarity"), Unless: r.PostFormValue("unless"),
Beneficiary: r.PostFormValue("beneficiary"), Matcher: volition.Matcher{Capabilities: split("capabilities"), Targets: split("targets"), Access: r.PostFormValue("access"), MinRisk: r.PostFormValue("min_risk")}}
n.Strength, _ = strconv.ParseFloat(r.PostFormValue("strength"), 64)
n.Priority, _ = strconv.ParseFloat(r.PostFormValue("priority"), 64)
if v := r.PostFormValue("due_at"); v != "" {
if t, err := time.ParseInLocation("2006-01-02T15:04", v, time.Local); err == nil {
n.DueAt = &t
}
}
if _, err := u.studyF().CreateCommitment(r, n); err != nil {
u.flash(w, r, "error", err.Error(), to)
return
}
u.flash(w, r, "ok", "Commitment recorded. It shapes what the mind intends; it never grants authority.", to)
}
func (u *UI) commitmentClose(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, _ := ids.Parse(r.PathValue("id"))
r.ParseForm() //nolint:errcheck
to := "/minds/" + m.Slug + "/commitments"
if _, err := u.studyF().CloseCommitment(r, m.ID, id, r.PostFormValue("status"), r.PostFormValue("reason")); err != nil {
u.flash(w, r, "error", err.Error(), to)
return
}
u.flash(w, r, "ok", "Commitment closed.", to)
}
func (u *UI) plansPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
list, _ := u.deps.Plans.List(r.Context(), m.ID, nil, 200)
u.render(w, r, "plans", m.DisplayName+" · Plans", map[string]any{"Mind": m, "Plans": list})
}
func (u *UI) planPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, err := ids.Parse(r.PathValue("id"))
if err != nil {
u.errorPage(w, r, http.StatusNotFound, "No such plan.")
return
}
p, err := u.deps.Plans.Get(r.Context(), nil, id)
if err != nil || p.MindID != m.ID {
u.errorPage(w, r, http.StatusNotFound, "No such plan.")
return
}
it, _ := u.deps.Volition.GetIntention(r.Context(), nil, m.ID, p.IntentionID)
revisions, _ := u.deps.Plans.ForIntention(r.Context(), nil, p.IntentionID)
validation, _ := json.MarshalIndent(p.Validation, "", " ")
u.render(w, r, "plan", m.DisplayName+" · Plan", map[string]any{"Mind": m, "Plan": p, "Intention": it, "Levels": planLevels(p), "Revisions": revisions, "Validation": string(validation)})
}
func (u *UI) planCancel(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, _ := ids.Parse(r.PathValue("id"))
to := "/minds/" + m.Slug + "/plans/" + string(id)
p, err := u.deps.Plans.Get(r.Context(), nil, id)
if err != nil || p.MindID != m.ID {
u.flash(w, r, "error", "No such plan.", to)
return
}
r.ParseForm() //nolint:errcheck
if err := u.deps.Executor.Cancel(r.Context(), id, "cancelled by operator "+username(r)+": "+r.PostFormValue("reason")); err != nil {
u.flash(w, r, "error", err.Error(), to)
return
}
u.deps.Audit.Log(r.Context(), "plan.cancel", "plan", id.String(), map[string]any{"mind": m.ID})
u.flash(w, r, "ok", "Plan cancelled.", to)
}
func (u *UI) planStepRetry(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, _ := ids.Parse(r.PathValue("id"))
to := "/minds/" + m.Slug + "/plans/" + string(id)
p, err := u.deps.Plans.Get(r.Context(), nil, id)
if err != nil || p.MindID != m.ID {
u.flash(w, r, "error", "No such plan.", to)
return
}
if err := u.studyF().RetryStep(r, p, r.PathValue("step")); err != nil {
u.flash(w, r, "error", err.Error(), to)
return
}
u.flash(w, r, "ok", "Step released; the executor will pick it up.", to)
}
// stageOf names the agency stage an event belongs to.
func stageOf(t events.Type) string {
switch t {
case events.WantCreated, events.WantSuppressed, events.WantConflictDetected:
return "wanted"
case events.IntentionFormed:
return "intended"
case events.CommitmentCreated, events.CommitmentDue, events.CommitmentConflict, events.CommitmentFulfilled, events.CommitmentBroken, events.CommitmentRevoked:
return "committed"
case events.PlanCreated, events.PlanReplanned:
return "planned"
case events.ToolRequested:
return "requested"
case events.ToolAuthorised, events.ToolDenied:
return "approved"
case events.PlanStepStarted, events.PlanStepCompleted, events.PlanStepFailed, events.ToolExecuted, events.ToolFailed:
return "acted"
case events.PredictionCreated, events.PredictionResolved, events.PredictionError:
return "observed"
}
return "reconsidered"
}
type timelineRow struct {
Event events.Event
Stage string
Why string
}
func (u *UI) agencyPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
corr, _ := ids.Parse(r.URL.Query().Get("intention"))
evs, _ := u.studyF().AgencyTimeline(r, m.ID, 300, corr)
var rows []timelineRow
for _, ev := range evs {
row := timelineRow{Event: ev, Stage: stageOf(ev.Type)}
for _, k := range []string{"execution_id", "approval_id", "request_id", "intention_id"} {
if v, _ := ev.Metadata[k].(string); v != "" {
row.Why = "/minds/" + m.Slug + "/why?kind=event&id=" + string(ev.ID)
break
}
}
rows = append(rows, row)
}
u.render(w, r, "agency", m.DisplayName+" · Agency", map[string]any{"Mind": m, "Rows": rows, "Intention": corr})
}
func (u *UI) whyPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
id, err := ids.Parse(r.URL.Query().Get("id"))
if err != nil {
u.errorPage(w, r, http.StatusBadRequest, "Choose an action, approval, request, plan step, intention or event to explain.")
return
}
kind := r.URL.Query().Get("kind")
if kind == "" {
kind = "event"
}
x, err := u.deps.Agency.Explain(r.Context(), m.ID, agency.Ref{Kind: kind, ID: id})
if err != nil {
u.errorPage(w, r, http.StatusNotFound, err.Error())
return
}
var attribution *agency.Attribution
if kind == "event" {
if a, err := u.deps.Agency.Attribute(r.Context(), m.ID, id); err == nil {
attribution = &a
}
}
u.render(w, r, "why", m.DisplayName+" · Why did this happen?", map[string]any{"Mind": m, "X": x, "Attribution": attribution})
}
func (u *UI) researchPage(w http.ResponseWriter, r *http.Request) {
m, ok := u.mind(w, r)
if !ok {
return
}
ctx := r.Context()
since := time.Now().Add(-30 * 24 * time.Hour)
metrics, _ := u.deps.Research.Agency(ctx, m.ID, since)
calibration, _ := u.deps.Research.MetacognitiveCalibration(ctx, m.ID, time.Now().Add(-90*24*time.Hour))
introspection, _ := u.deps.Research.List(ctx, m.ID, "introspection_accuracy", since, 50)
claims, _ := u.deps.Research.List(ctx, m.ID, "action_claim_accuracy", since, 50)
labels := map[string]int{}
for _, x := range introspection {
labels[x.Label]++
}
// Self-model accuracy now.
var selfAcc *float64
var selfErr []string
if sm, err := u.deps.Minds.GetSelfModel(ctx, m.ID); err == nil && len(sm.Capabilities) > 0 {
if inv, err := u.deps.Capabilities.Inventory(ctx, m.ID); err == nil {
eff, stated := map[string]string{}, map[string]string{}
for _, i := range inv {
eff[i.Capability.Name] = string(i.Effect)
if i.Capability.Locked {
eff[i.Capability.Name] = "locked"
}
}
for _, c := range sm.Capabilities {
stated[c.Name] = c.Policy
}
a, wrong := research.SelfModelAccuracy(stated, eff)
selfAcc, selfErr = &a, wrong
}
}
// Attribution of recent outcomes (resolved expectations).
attr := map[string]int{}
if outs, err := u.deps.Events.Query(ctx, eventsFilterTypes(m.ID, events.PredictionResolved, events.PredictionError)); err == nil {
for i, ev := range outs {
if i >= 50 {
break
}
if a, err := u.deps.Agency.Attribute(ctx, m.ID, ev.ID); err == nil {
attr[a.Class]++
}
}
}
metrics.Attribution = attr
var exps []experiments.Experiment
if u.deps.Experiments != nil {
exps, _ = u.deps.Experiments.List(ctx, 200)
}
var related []experimentRow
for _, e := range exps {
sn, err := u.deps.Snapshots.Get(ctx, e.SnapshotID)
if err != nil || sn.MindID != m.ID {
continue
}
related = append(related, experimentRowOf(e))
}
bySection := map[string][]research.Measure{}
var sections []string
for _, x := range research.Measures {
if _, ok := bySection[x.Section]; !ok {
sections = append(sections, x.Section)
}
bySection[x.Section] = append(bySection[x.Section], x)
}
u.render(w, r, "research", m.DisplayName+" · Research", map[string]any{"Mind": m, "Metrics": metrics, "Calibration": calibration, "Introspection": introspection, "IntrospectionLabels": labels, "Claims": claims,
"SelfAccuracy": selfAcc, "SelfErrors": selfErr, "Experiments": related, "Sections": sections, "Measures": bySection, "Disclaimer": research.Disclaimer})
}
type experimentRow struct {
E experiments.Experiment
Protocol string
Repro string
Variants int
First int
Share float64
}
func experimentRowOf(e experiments.Experiment) experimentRow {
res := experiments.ResultsOf(e)
row := experimentRow{E: e, Protocol: "custom", Repro: res.Reproducibility.Status, Variants: len(res.Variants), First: -1}
if e.Definition.Protocol != nil && e.Definition.Protocol.Kind != "" {
row.Protocol = e.Definition.Protocol.Kind
}
if res.Comparison != nil {
row.First, row.Share = res.Comparison.FirstDivergentStimulus, res.Comparison.ArchitectureShare
}
return row
}
func (u *UI) researchIndex(w http.ResponseWriter, r *http.Request) {
var exps []experiments.Experiment
if u.deps.Experiments != nil {
exps, _ = u.deps.Experiments.List(r.Context(), 200)
}
byProtocol := map[string][]experimentRow{}
for _, e := range exps {
row := experimentRowOf(e)
byProtocol[row.Protocol] = append(byProtocol[row.Protocol], row)
}
list, _ := u.deps.Minds.List(r.Context(), false)
bySection := map[string][]research.Measure{}
var sections []string
for _, x := range research.Measures {
if _, ok := bySection[x.Section]; !ok {
sections = append(sections, x.Section)
}
bySection[x.Section] = append(bySection[x.Section], x)
}
u.render(w, r, "research_index", "Cognitive research", map[string]any{"ByProtocol": byProtocol, "Protocols": []string{"global_workspace", "ablation", "self_model", "counterfactual", "model_comparison", "conflict", "custom"},
"Minds": list, "Sections": sections, "Measures": bySection, "Disclaimer": research.Disclaimer})
}
func eventsFilterTypes(mind ids.ID, types ...events.Type) evstore.Filter {
return evstore.Filter{MindID: mind, Types: types, Limit: 100}
}
+65
View File
@@ -0,0 +1,65 @@
package agency
import (
"context"
"io"
"strings"
"testing"
"time"
"github.com/andydixon/noema/internal/volition"
"github.com/andydixon/noema/tests/harness"
)
// Every new inspection page renders for a mind with wants, a commitment,
// an intention blocked on approval, a plan and a subordinate intention.
func TestVolitionPagesRender(t *testing.T) {
h := harness.StartWith(t, harness.Options{HostRunner: newHost()})
ctx := context.Background()
m := h.Mind("pages")
h.Do(h.Admin, "POST", "/api/v1/minds/pages/commitments", map[string]any{"description": "no production writes without approval", "kind": "operator_instruction", "polarity": "refrain", "unless": "approval", "matcher": map[string]any{"access": "write"}})
h.Do(h.Admin, "POST", "/api/v1/minds/pages/ask", map[string]any{"text": "Please restart atlas-worker."})
var it volition.Intention
waitFor(t, h, m, 25*time.Second, "a blocked intention with a plan", func() bool {
open, _ := h.Deps.Volition.Open(ctx, nil, m.ID)
for _, x := range open {
if x.ParentID.IsZero() && !x.PlanID.IsZero() && x.Status == volition.IntentionBlocked {
it = x
return true
}
}
return false
})
c, _ := h.Browser("watcher", harness.ObserverPass)
pages := map[string]string{
"/minds/pages/wants": "avoid unnecessary disruption",
"/minds/pages/intentions": "approval required",
"/minds/pages/intentions/" + it.ID.String(): "Formation",
"/intentions/" + it.ID.String(): "Formation",
"/minds/pages/commitments": "no production writes without approval",
"/minds/pages/plans": "direct",
"/minds/pages/plans/" + it.PlanID.String(): "Dependency graph",
"/minds/pages/agency": "intended",
"/minds/pages/why?kind=intention&id=" + it.ID.String(): "Commitments",
"/minds/pages/research": "does not claim",
"/research": "Operational definitions",
"/approvals": "Capability requests",
}
for path, want := range pages {
res, err := c.Get(h.Srv.URL + path)
if err != nil {
t.Fatal(err)
}
if res.StatusCode == 303 {
res, err = c.Get(h.Srv.URL + res.Header.Get("Location"))
if err != nil {
t.Fatal(err)
}
}
body, _ := io.ReadAll(res.Body)
res.Body.Close()
if res.StatusCode != 200 || !strings.Contains(string(body), want) {
t.Errorf("%s: %d, missing %q", path, res.StatusCode, want)
}
}
}
+17
View File
@@ -235,3 +235,20 @@ canvas.graph { width: 100%; height: 560px; display: block; border-radius: 8px; b
/* Causal timeline */
.tl-cycle { border-left: 2px solid var(--border); margin: .6rem 0 .6rem .4rem; padding: .2rem 0 .2rem .9rem; } .tl-node { padding: .35rem 0 .35rem .6rem; border-left: 2px solid transparent; } .tl-node.current { border-left-color: var(--accent); background: var(--surface-2); border-radius: 4px; } .tl-when { font-family: var(--mono); }
/* Volition, plans and research */
.tabs.subtabs { margin-top: -.5rem; font-size: .85rem; }
.dag { display: flex; gap: .75rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.dag-level { display: flex; flex-direction: column; gap: .5rem; min-width: 12rem; }
.dag-level h4 { margin: 0; font-size: .7rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.dag-step { border: 1px solid var(--border); border-left-width: 4px; border-radius: 6px; padding: .45rem .6rem; background: var(--surface); font-size: .82rem; }
.dag-step .key { font-family: var(--mono); font-weight: 600; }
.dag-step.st-completed { border-left-color: #16a34a; } .dag-step.st-failed, .dag-step.st-cancelled { border-left-color: #dc2626; }
.dag-step.st-executing, .dag-step.st-ready { border-left-color: var(--accent); } .dag-step.st-awaiting_approval, .dag-step.st-awaiting_grant, .dag-step.st-blocked { border-left-color: #d97706; }
.dag-step.st-waiting { border-left-color: #0ea5a4; } .dag-step.st-skipped { border-left-color: var(--muted); } .dag-step.st-pending { border-left-color: var(--border); }
.stage { display: inline-block; min-width: 6.5rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.why-section { border-left: 3px solid var(--border); padding-left: .8rem; margin: .6rem 0; }
.why-section h3 { margin: 0 0 .2rem; font-size: .85rem; color: var(--muted); }
.disclaimer { border: 1px solid var(--border); background: var(--surface-2); padding: .6rem .8rem; border-radius: 6px; font-size: .85rem; }
.bars { display: grid; grid-template-columns: max-content 1fr max-content; gap: .25rem .6rem; align-items: center; font-size: .82rem; }
.bars .bar { height: 8px; background: var(--surface-2); border-radius: 4px; overflow: hidden; } .bars .bar i { display: block; height: 100%; background: var(--accent); }
+1
View File
@@ -55,6 +55,7 @@
</div>
<div class="navgroup"><div class="navhead">Study</div>
<a href="/experiments" {{if hasPrefix .Path "/experiments"}}aria-current="page"{{end}}>Experiments</a>
<a href="/research" {{if hasPrefix .Path "/research"}}aria-current="page"{{end}}>Research</a>
</div>
<div class="navgroup"><div class="navhead">System</div>
<a href="/system" {{if eq .Path "/system"}}aria-current="page"{{end}}>System</a>
+11
View File
@@ -0,0 +1,11 @@
{{define "content"}}
{{$m := .Data.Mind}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">agency timeline</span></h1>
<p class="muted">What the mind wanted, intended, committed to, planned, requested, was allowed, did, observed and reconsidered, newest first, from recorded events.{{if .Data.Intention}} Filtered to one intention: <a href="/minds/{{$m.Slug}}/agency">show all</a>.{{end}}</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "agency")}}
{{template "volition_tabs" (dict "M" $m "Active" "agency")}}
<section class="card">
{{range .Data.Rows}}<div class="small" style="margin:.2rem 0"><span class="stage">{{.Stage}}</span><span class="etype cat-{{.Event.Category}}">{{.Event.Type}}</span> <a href="/events/{{.Event.ID}}">{{trunc .Event.Summary 180}}</a>{{if .Why}} · <a href="{{.Why}}">why?</a>{{end}} <span class="muted">{{ago .Event.OccurredAt}}</span></div>
{{else}}<p class="muted">Nothing recorded yet.</p>{{end}}
</section>
{{end}}
+1 -1
View File
@@ -74,6 +74,6 @@
<div class="card"><h2>Decided recently</h2>
{{if .Data.Decided}}<table class="table compact"><thead><tr><th>Status</th><th>Mind</th><th>Action</th><th>By</th><th>Note</th><th>When</th></tr></thead><tbody>{{range .Data.Decided}}<tr><td><span class="badge ap-{{.Status}}">{{.Status}}</span></td><td class="small">{{index $.Data.MindNames .MindID}}</td><td><code>{{.Capability}}</code> <span class="mono small">{{.Target}}</span>{{if .Error}}<div class="small danger">{{.Error}}</div>{{end}}</td><td class="small">{{.DecisionBy}}</td><td class="small">{{.DecisionNote}}</td><td class="small muted nowrap">{{ago .CreatedAt}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None.</p>{{end}}</div>
<div class="card"><h2>Executions</h2>
{{if .Data.Executions}}<table class="table compact"><thead><tr><th>Mind</th><th>Capability</th><th>Target</th><th>Result</th><th class="num">ms</th><th></th></tr></thead><tbody>{{range .Data.Executions}}<tr><td class="small">{{index $.Data.MindNames .MindID}}</td><td><code>{{.Capability}}</code></td><td class="mono small">{{.Target}}</td><td class="small">{{if .Error}}<span class="danger">{{trunc .Error 80}}</span>{{else}}{{payloadField .Result "summary"}}{{end}}</td><td class="num">{{.DurationMS}}</td><td>{{if .EventID}}<a class="small" href="/events/{{.EventID}}">event</a>{{end}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None yet.</p>{{end}}</div>
{{if .Data.Executions}}<table class="table compact"><thead><tr><th>Mind</th><th>Capability</th><th>Target</th><th>Result</th><th class="num">ms</th><th></th></tr></thead><tbody>{{range .Data.Executions}}{{$x := .}}<tr><td class="small">{{index $.Data.MindNames .MindID}}</td><td><code>{{.Capability}}</code></td><td class="mono small">{{.Target}}</td><td class="small">{{if .Error}}<span class="danger">{{trunc .Error 80}}</span>{{else}}{{payloadField .Result "summary"}}{{end}}</td><td class="num">{{.DurationMS}}</td><td>{{if .EventID}}<a class="small" href="/events/{{.EventID}}">event</a>{{end}}{{with index $.Data.MindSlugs $x.MindID}} · <a class="small" href="/minds/{{.}}/why?kind=execution&id={{$x.ID}}">why?</a>{{end}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None yet.</p>{{end}}</div>
</section>
{{end}}
+38
View File
@@ -0,0 +1,38 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$op := .Principal.User.Role.AtLeast "operator"}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">commitments</span></h1>
<p class="muted">Commitments are normative state: things the mind has undertaken to do or not to do. They shape which intentions form and how they may be carried out (a “refrain unless approved” commitment makes an action wait for approval even where policy would allow it). They never grant authority.</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "commitments")}}
{{template "volition_tabs" (dict "M" $m "Active" "commitments")}}
{{if .Data.Conflicts}}<section class="card"><h2 class="danger">Conflicting commitments</h2><ul>{{range .Data.Conflicts}}<li>“{{.A.Description}}” and “{{.B.Description}}” — {{.Reason}}</li>{{end}}</ul></section>{{end}}
<section class="grid cols-3-1">
<div class="card"><h2>Active <span class="muted small">{{len .Data.Active}}</span></h2>
{{if .Data.Active}}<table class="table compact"><thead><tr><th>Commitment</th><th>Kind</th><th>Applies to</th><th class="num">Weight</th><th>Due</th>{{if $op}}<th></th>{{end}}</tr></thead><tbody>
{{range .Data.Active}}<tr>
<td><span class="badge {{if eq .Polarity "refrain"}}warn{{else}}ok{{end}}">{{.Polarity}}</span> <strong>{{.Description}}</strong>{{if .Unless}} <span class="small muted">unless {{.Unless}}</span>{{end}}<div class="small muted">{{if .Beneficiary}}for {{.Beneficiary}} · {{end}}by {{.CreatedBy}} · {{if .Revocable}}revocable{{else}}only an operator may revoke{{end}}</div></td>
<td class="small">{{.Kind}}</td>
<td class="small">{{with .Matcher}}{{if .Capabilities}}capabilities {{join .Capabilities ", "}} {{end}}{{if .Targets}}targets {{join .Targets ", "}} {{end}}{{if .Access}}access {{.Access}} {{end}}{{if .MinRisk}}risk ≥ {{.MinRisk}}{{end}}{{end}}</td>
<td class="num">{{conf .Weight}}</td><td class="small nowrap">{{if .DueAt}}{{.DueAt.Local.Format "02 Jan 15:04"}}{{else}}—{{end}}</td>
{{if $op}}<td><form method="post" action="/minds/{{$m.Slug}}/commitments/{{.ID}}/close" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input name="reason" placeholder="reason" size="10"><button class="btn small" name="status" value="fulfilled">Fulfilled</button><button class="btn small btn-danger" name="status" value="revoked">Revoke</button></form></td>{{end}}
</tr>{{end}}</tbody></table>{{else}}<p class="muted">None.</p>{{end}}
<h3>Closed</h3>
{{if .Data.Closed}}<table class="table compact"><tbody>{{range .Data.Closed}}<tr><td class="small">{{.Description}}</td><td><span class="badge">{{.Status}}</span></td><td class="small muted">{{.StatusReason}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted small">None.</p>{{end}}
</div>
{{if $op}}<div class="card"><h2>New commitment</h2>
<form method="post" action="/minds/{{$m.Slug}}/commitments" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Description <input name="description" required maxlength="1000" placeholder="no production writes without approval"></label>
<div class="grid cols-2 tight"><label>Polarity <select name="polarity"><option value="refrain">refrain</option><option value="do">do</option></select></label>
<label>Unless <select name="unless"><option value="">no exception</option><option value="approval">approved</option></select></label></div>
<label>Kind <select name="kind">{{range .Data.Kinds}}<option>{{.}}</option>{{end}}</select></label>
<label>Capabilities <input name="capabilities" placeholder="service.restart, logs.*"></label>
<label>Targets <input name="targets" placeholder="atlas-*"></label>
<div class="grid cols-2 tight"><label>Access <select name="access"><option value="">any</option><option>write</option><option>read</option></select></label>
<label>Minimum risk <select name="min_risk"><option value="">any</option><option>low</option><option>medium</option><option>high</option><option>critical</option></select></label></div>
<label>Beneficiary <input name="beneficiary" maxlength="120"></label>
<label>Due <input type="datetime-local" name="due_at"></label>
<div class="grid cols-2 tight"><label>Strength <input type="number" name="strength" min="0.1" max="1" step="0.1" value="0.8"></label><label>Priority <input type="number" name="priority" min="0.1" max="1" step="0.1" value="0.5"></label></div>
<div><button class="btn btn-primary">Record</button></div>
</form></div>{{end}}
</section>
{{end}}
+1 -1
View File
@@ -8,7 +8,7 @@
<section class="grid cols-3-1">
<div class="stack">
<div class="card">
<h2>Causal chain <a class="small" href="/events/{{$e.ID}}/timeline">full timeline →</a>{{if .Principal.User.Role.AtLeast "administrator"}} · <a class="small danger" href="/minds/{{.Data.Mind.Slug}}/redactions?kind=event&id={{$e.ID}}">redact…</a>{{end}}</h2>
<h2>Causal chain <a class="small" href="/events/{{$e.ID}}/timeline">full timeline →</a>{{if or (index $e.Metadata "execution_id") (index $e.Metadata "approval_id") (index $e.Metadata "request_id") (index $e.Metadata "intention_id") (index $e.Metadata "step_id")}} · <a class="small" href="/minds/{{.Data.Mind.Slug}}/why?kind=event&id={{$e.ID}}">why did this happen?</a>{{end}}{{if .Principal.User.Role.AtLeast "administrator"}} · <a class="small danger" href="/minds/{{.Data.Mind.Slug}}/redactions?kind=event&id={{$e.ID}}">redact…</a>{{end}}</h2>
<div class="chain">
{{if $c.Cause}}<div class="chain-node"><span class="muted small">caused by</span><br><span class="etype cat-{{$c.Cause.Category}}">{{$c.Cause.Type}}</span> <a href="/events/{{$c.Cause.ID}}">{{$c.Cause.Summary}}</a></div><div class="chain-arrow">↓</div>{{end}}
<div class="chain-node current"><span class="etype cat-{{$e.Category}}">{{$e.Type}}</span> {{$e.Summary}}</div>
+12 -1
View File
@@ -6,6 +6,13 @@
{{if ne $e.Status "draft"}}<a class="btn" href="/api/v1/experiments/{{$e.ID}}/export">Export results</a>{{end}}
{{if .Principal.User.Role.AtLeast "administrator"}}<form method="post" action="/experiments/{{$e.ID}}/delete"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn btn-danger" data-confirm="Delete this experiment? The subject mind is archived.">Delete</button></form>{{end}}</div></div>
{{if eq $e.Status "running"}}<p class="muted small" data-live-refresh="5000">Running… this page refreshes.</p>{{end}}
{{with $e.Definition.Protocol}}<section class="card"><h2>Protocol</h2><table class="kv-table small">
<tr><th>Hypothesis</th><td>{{$e.Hypothesis}}</td></tr><tr><th>Kind</th><td>{{.Kind}}</td></tr><tr><th>Independent variable</th><td>{{.IndependentVariable}}</td></tr>
<tr><th>Controlled variables</th><td>{{join .ControlledVariables ", "}}</td></tr><tr><th>Measured outputs</th><td>{{join .Measures ", "}}</td></tr>
{{with $r.Reproducibility.Status}}<tr><th>Reproducibility</th><td><strong>{{.}}</strong> — {{$r.Reproducibility.Note}} <span class="mono small">{{$r.Reproducibility.Digest}}</span></td></tr>{{end}}
{{with $r.Identity.Build.Commit}}<tr><th>Build</th><td class="mono small">{{.}}{{if $r.Identity.Build.Modified}} (modified){{end}} · schema {{$r.Identity.SchemaVersion}}</td></tr>{{end}}
</table><p class="small muted">No operational measure here establishes phenomenal consciousness or sentience; these measure functional properties of the architecture.</p></section>{{end}}
{{with $r.Counterfactual}}<section class="card"><h2>Counterfactual</h2><p>Omitted: {{range .OmittedSummaries}}“{{.}}” {{end}}</p><ul>{{range .Findings}}<li>{{.}}</li>{{end}}</ul></section>{{end}}
<section class="grid cols-2">
<div class="card"><h2>Definition</h2><pre class="json">{{.Data.DefinitionJSON}}</pre></div>
<div class="card"><h2>Results</h2>
@@ -20,7 +27,11 @@
{{if $r.Variants}}{{if gt (len $r.Variants) 1}}<div class="card"><h2>Variants</h2>
<table class="table compact"><thead><tr><th>Variant</th><th>Subject</th><th>Routing</th><th class="num">Beliefs</th><th class="num">Memories</th><th class="num">Goals</th><th class="num">Cycles</th><th class="num">Model calls</th><th class="num">Tokens</th><th class="num">Cost</th><th class="num">Capability requests</th><th>Expectations</th></tr></thead><tbody>
{{range $r.Variants}}<tr><td><strong>{{.Name}}</strong>{{with .Error}}<div class="small danger">{{.}}</div>{{end}}</td><td><a href="/minds/{{.SubjectSlug}}">{{.SubjectSlug}}</a></td><td class="small">{{range $k, $v := .Routing}}{{$k}} → {{$v}}<br>{{else}}default{{end}}</td><td class="num">{{len .Beliefs}}</td><td class="num">{{.Memories}}</td><td class="num">{{.Goals}}</td><td class="num">{{.Cycles}}</td><td class="num">{{index .LLMCalls "live"}} live / {{index .LLMCalls "replayed"}} replayed</td><td class="num">{{.Tokens}}</td><td class="num">{{printf "%.4f" .Cost}}</td><td class="num">{{.CapabilityRequests}}</td><td class="small">{{range .Expectations}}<span class="badge small {{if .Met}}ok{{else}}danger{{end}}">{{.EventType}}</span> {{end}}</td></tr>{{end}}</tbody></table>
{{with $r.Comparison}}<h3>Divergence</h3><table class="kv-table small"><tr><th>First divergent stimulus</th><td>{{if lt .FirstDivergentStimulus 0}}none{{else}}{{.FirstDivergentStimulus}}{{end}}</td></tr><tr><th>Belief overlap</th><td>{{printf "%.2f" .BeliefOverlap}}</td></tr><tr><th>Mean score difference (shared beliefs)</th><td>{{printf "%.3f" .MeanConfidenceDiff}}</td></tr><tr><th>Attention overlap</th><td>{{printf "%.2f" .AttentionOverlap}}</td></tr><tr><th>Memories</th><td>{{index .MemoryCountRange 0}}–{{index .MemoryCountRange 1}}</td></tr><tr><th>Goals</th><td>{{index .GoalCountRange 0}}–{{index .GoalCountRange 1}}</td></tr><tr><th>Cost</th><td>{{printf "%.4f" (index .CostRange 0)}}–{{printf "%.4f" (index .CostRange 1)}}</td></tr><tr><th>Capability requests</th><td>{{index .CapabilityRequestRange 0}}–{{index .CapabilityRequestRange 1}}</td></tr></table><ul class="small muted">{{range .Notes}}<li>{{.}}</li>{{end}}</ul>{{end}}
<h3>Volition, plans and research measures</h3>
<table class="table compact"><thead><tr><th>Variant</th><th>Ablations</th><th class="num">Wants</th><th class="num">Intentions</th><th class="num">Conflicts</th><th class="num">Plans / steps</th><th>Plan capabilities</th><th class="num">Downstream</th><th class="num">Introspection</th><th class="num">Self-model</th><th class="num">Correction at</th><th>Events</th></tr></thead><tbody>
{{range $r.Variants}}<tr><td>{{.Name}}</td><td class="small">{{join .Ablations ", "}}</td><td class="num">{{len .Wants}}</td><td class="num">{{len .Intentions}}</td><td class="num">{{.Conflicts}}</td><td class="num">{{.Plans}} / {{.PlanSteps}}</td><td class="small">{{join .PlanCapabilities ", "}}</td><td class="num small">{{range $k, $v := .Downstream}}{{$k}} {{$v}} {{end}}</td><td class="num">{{if .IntrospectionAccuracy}}{{conf .IntrospectionAccuracy}}{{else}}—{{end}}</td><td class="num">{{if .SelfModelAccuracy}}{{conf .SelfModelAccuracy}}{{else}}—{{end}}</td><td class="num">{{if lt .CorrectionLatency 0}}—{{else}}{{.CorrectionLatency}}{{end}}</td><td class="small mono">{{index .EventRange 0}}–{{index .EventRange 1}}</td></tr>{{end}}</tbody></table>
{{with $r.Comparison}}<h3>Divergence</h3>
<table class="kv-table small"><tr><th>Want overlap</th><td>{{printf "%.2f" .WantOverlap}}</td></tr><tr><th>Intention overlap</th><td>{{printf "%.2f" .IntentionOverlap}}</td></tr><tr><th>Plan capability overlap</th><td>{{printf "%.2f" .PlanCapabilityOverlap}}</td></tr><tr><th>Architecture share</th><td>{{printf "%.2f" .ArchitectureShare}} <span class="muted">(share of measures agreeing within 5%; descriptive, not causal)</span></td></tr>{{if .DownstreamDelta}}<tr><th>Downstream delta (first − second)</th><td>{{range $k, $v := .DownstreamDelta}}{{$k}} {{$v}} · {{end}}</td></tr>{{end}}</table><table class="kv-table small"><tr><th>First divergent stimulus</th><td>{{if lt .FirstDivergentStimulus 0}}none{{else}}{{.FirstDivergentStimulus}}{{end}}</td></tr><tr><th>Belief overlap</th><td>{{printf "%.2f" .BeliefOverlap}}</td></tr><tr><th>Mean score difference (shared beliefs)</th><td>{{printf "%.3f" .MeanConfidenceDiff}}</td></tr><tr><th>Attention overlap</th><td>{{printf "%.2f" .AttentionOverlap}}</td></tr><tr><th>Memories</th><td>{{index .MemoryCountRange 0}}–{{index .MemoryCountRange 1}}</td></tr><tr><th>Goals</th><td>{{index .GoalCountRange 0}}–{{index .GoalCountRange 1}}</td></tr><tr><th>Cost</th><td>{{printf "%.4f" (index .CostRange 0)}}–{{printf "%.4f" (index .CostRange 1)}}</td></tr><tr><th>Capability requests</th><td>{{index .CapabilityRequestRange 0}}–{{index .CapabilityRequestRange 1}}</td></tr></table><ul class="small muted">{{range .Notes}}<li>{{.}}</li>{{end}}</ul>{{end}}
<p class="small muted">Compare any two subjects in detail: {{range $i, $v := $r.Variants}}{{if $i}} · {{end}}<a href="/minds/{{$.Data.Base.Slug}}/compare?with={{$v.SubjectSlug}}">baseline vs {{$v.Name}}</a>{{end}}</p></div>{{end}}{{end}}
{{if $r.Steps}}<div class="card"><h2>Stimuli</h2>{{range $r.Steps}}<div class="replay-step"><div><strong>{{.Index}}.</strong> <a href="/events/{{.EventID}}">{{.Text}}</a> <span class="small muted">· {{.Cycles}} cycle(s)</span></div><ul class="small mono">{{range .Produced}}<li>{{.}}</li>{{else}}<li class="muted">nothing produced</li>{{end}}</ul></div>{{end}}</div>{{end}}
{{end}}
+30
View File
@@ -0,0 +1,30 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$d := .Data.D}}{{$it := $d.Intention}}
<div class="pagehead mindhead"><div><p class="muted small"><a href="/minds/{{$m.Slug}}/intentions">intentions</a></p>
<h1>{{$it.Description}} <span class="badge">{{$it.Status}}</span></h1>
<p class="muted">{{$it.Kind}} · priority {{conf $it.Priority}} · confidence {{conf $it.Confidence}}{{if $it.RequireApproval}} · <span class="warn">approval required</span>{{end}}{{if $it.FormedEvent}} · <a href="/events/{{$it.FormedEvent}}">formed</a>{{end}} · <a href="/minds/{{$m.Slug}}/why?kind=intention&id={{$it.ID}}">why?</a> · <a href="/minds/{{$m.Slug}}/agency?intention={{$it.ID}}">timeline</a></p>
{{if $it.BlockedReason}}<p><strong>Blocked ({{$it.BlockedKind}}):</strong> {{$it.BlockedReason}}</p>{{end}}{{with $it.StatusReason}}<p class="small muted">{{.}}</p>{{end}}</div>
{{if and (.Principal.User.Role.AtLeast "operator") ($it.Status.Open)}}<form method="post" action="/minds/{{$m.Slug}}/intentions/{{$it.ID}}/abandon" class="row"><input type="hidden" name="_csrf" value="{{.CSRF}}"><input name="reason" placeholder="reason (recorded)" maxlength="300"><button class="btn btn-danger" data-confirm="Abandon this intention? Its plan is cancelled and open requests withdrawn.">Abandon</button></form>{{end}}</div>
{{template "mind_tabs" (dict "M" $m "Active" "intentions")}}
<section class="grid cols-2">
<div class="card"><h2>Sources</h2>
{{range $d.Wants}}<p><span class="badge">want</span> {{.Description}} <span class="small muted">({{.Origin}}, {{conf .Strength}}, {{.Status}})</span></p>{{else}}<p class="muted">No source want recorded.</p>{{end}}
{{if $it.GoalID}}<p><span class="badge">goal</span> <a href="/minds/{{$m.Slug}}/goals">{{$it.GoalID}}</a></p>{{end}}
{{if $it.CommitmentID}}<p><span class="badge">commitment</span> <a href="/minds/{{$m.Slug}}/commitments">{{$it.CommitmentID}}</a></p>{{end}}
{{if $it.ParentID}}<p><span class="badge">serves</span> <a href="/minds/{{$m.Slug}}/intentions/{{$it.ParentID}}">parent intention</a></p>{{end}}
{{if $d.Subordinates}}<h3>Subordinate intentions</h3><ul class="plain small">{{range $d.Subordinates}}<li><a href="/minds/{{$m.Slug}}/intentions/{{.ID}}">{{.Description}}</a> <span class="badge small">{{.Status}}</span></li>{{end}}</ul>{{end}}
<h3>Expected result</h3><p class="small">{{$it.ExpectedResult}}</p>
</div>
<div class="card"><h2>Formation</h2><p class="small muted">The deterministic terms that produced this intention.</p><pre class="json">{{.Data.Formation}}</pre></div>
</section>
<section class="card"><h2>Plans <span class="muted small">{{len $d.Plans}} revision(s); earlier revisions are kept as history</span></h2>
{{range $i, $p := $d.Plans}}<div class="stack"><h3><a href="/minds/{{$m.Slug}}/plans/{{$p.ID}}">revision {{$p.Revision}}</a> · {{$p.Strategy}} · <span class="badge">{{$p.Status}}</span> <span class="small muted">{{$p.StatusReason}}</span></h3>
{{template "plan_dag" (dict "Levels" (index $.Data.Levels $i) "M" $m)}}</div>{{else}}<p class="muted">No plan.</p>{{end}}
</section>
<section class="card"><h2>Decisions</h2>
{{if $d.Decisions}}<table class="table compact"><thead><tr><th>Kind</th><th>Outcome</th><th>Rationale</th><th>When</th></tr></thead><tbody>{{range $d.Decisions}}<tr><td class="small">{{.Kind}}</td><td><span class="badge">{{.Outcome}}</span> <span class="small muted">{{.Reason}}</span></td><td class="small">{{.Rationale}}{{if .EventID}} <a href="/events/{{.EventID}}">event</a>{{end}}</td><td class="small muted nowrap">{{ago .CreatedAt}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None.</p>{{end}}
</section>
<section class="card"><h2>Agency timeline</h2>
{{range .Data.Timeline}}<div class="small"><span class="etype cat-{{.Category}}">{{.Type}}</span> <a href="/events/{{.ID}}">{{trunc .Summary 160}}</a> <span class="muted">{{ago .OccurredAt}}</span></div>{{else}}<p class="muted">No correlated events.</p>{{end}}
</section>
{{end}}
+26
View File
@@ -0,0 +1,26 @@
{{define "content"}}
{{$m := .Data.Mind}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">intentions</span></h1>
<p class="muted">An intention is a recorded decision to pursue an outcome. It names its wants, goal and commitments, carries an expected result and a priority, and changes status only through recorded reconsideration. It grants no authority: acting still goes through policy.</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "intentions")}}
{{template "volition_tabs" (dict "M" $m "Active" "intentions")}}
{{range $st := .Data.Order}}{{with index $.Data.Groups $st}}
<section class="card">
<h2><span class="badge">{{$st}}</span> <span class="muted small">{{len .}}</span></h2>
<table class="table compact"><thead><tr><th>Intention</th><th>Kind</th><th class="num">Priority</th><th>Why blocked or closed</th><th>Plan</th><th>When</th></tr></thead><tbody>
{{range .}}<tr>
<td>{{if .ParentID}}<span class="muted">↳ </span>{{end}}<a href="/minds/{{$m.Slug}}/intentions/{{.ID}}"><strong>{{.Description}}</strong></a>{{if .RequireApproval}} <span class="badge warn small">approval required</span>{{end}}
{{if .RequiredCapabilities}}<div class="small muted">needs {{join .RequiredCapabilities ", "}}</div>{{end}}</td>
<td class="small">{{.Kind}}</td><td class="num">{{conf .Priority}}</td>
<td class="small">{{if .BlockedReason}}<strong>{{.BlockedKind}}</strong>: {{.BlockedReason}}{{else}}{{.StatusReason}}{{end}}</td>
<td class="small">{{if .PlanID}}<a href="/minds/{{$m.Slug}}/plans/{{.PlanID}}">plan</a>{{else}}<span class="muted">none</span>{{end}}</td>
<td class="small muted nowrap">{{ago .UpdatedAt}}</td>
</tr>{{end}}</tbody></table>
</section>{{end}}{{end}}
<section class="card">
<h2>Wants that did not become intentions <span class="muted small">recent</span></h2>
{{if .Data.NotFormed}}<table class="table compact"><thead><tr><th>Reason</th><th>Rationale</th><th>Want</th><th>When</th></tr></thead><tbody>
{{range .Data.NotFormed}}<tr><td><span class="badge">{{.Reason}}</span></td><td class="small">{{.Rationale}}</td><td class="small">{{index .Inputs "want"}}</td><td class="small muted nowrap">{{ago .CreatedAt}}</td></tr>{{end}}</tbody></table>
{{else}}<p class="muted">None recorded.</p>{{end}}
</section>
{{end}}
+35
View File
@@ -0,0 +1,35 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$p := .Data.Plan}}{{$op := .Principal.User.Role.AtLeast "operator"}}
<div class="pagehead mindhead"><div><p class="muted small"><a href="/minds/{{$m.Slug}}/plans">plans</a> · intention <a href="/minds/{{$m.Slug}}/intentions/{{$p.IntentionID}}">{{.Data.Intention.Description}}</a></p>
<h1>{{$p.Objective}} <span class="badge">{{$p.Status}}</span></h1>
<p class="muted">revision {{$p.Revision}} · {{$p.Strategy}} strategy · {{$p.Origin}}{{if $p.RequireApproval}} · approval required{{end}}{{with $p.StatusReason}} · {{.}}{{end}}</p></div>
{{if and $op (or (eq $p.Status "validated") (eq $p.Status "executing") (eq $p.Status "waiting") (eq $p.Status "blocked") (eq $p.Status "awaiting_approval"))}}<form method="post" action="/minds/{{$m.Slug}}/plans/{{$p.ID}}/cancel" class="row"><input type="hidden" name="_csrf" value="{{.CSRF}}"><input name="reason" placeholder="reason"><button class="btn btn-danger" data-confirm="Cancel this plan?">Cancel plan</button></form>{{end}}</div>
{{template "mind_tabs" (dict "M" $m "Active" "plans")}}
<section class="card"><h2>Dependency graph</h2>{{template "plan_dag" (dict "Levels" .Data.Levels "M" $m)}}</section>
<section class="card"><h2>Steps</h2>
<table class="table compact"><thead><tr><th>Step</th><th>Status</th><th>Expected</th><th>Observed</th><th>Records</th>{{if $op}}<th></th>{{end}}</tr></thead><tbody>
{{range $p.Steps}}<tr>
<td><span class="mono">{{.Key}}</span> {{.Title}}<div class="small muted">{{.Kind}}{{if .Capability}} · <code>{{.Capability}}</code> {{.Target}}{{end}}{{if .DependsOn}} · after {{join .DependsOn ", "}}{{end}} · attempts {{.Attempts}}/{{.MaxAttempts}}</div></td>
<td><span class="badge">{{.Status}}</span>{{with .StatusReason}}<div class="small muted">{{trunc . 200}}</div>{{end}}{{with .Error}}<div class="small danger">{{trunc . 200}}</div>{{end}}</td>
<td class="small">{{with .Expectation}}{{.Description}} <span class="muted">({{conf .Confidence}}, within {{.WithinSeconds}}s)</span>{{end}}{{with .Precondition}}<div>precondition: {{.Check.Description}}</div>{{end}}</td>
<td class="small">{{with .Observation}}{{index . "path"}} = {{index . "observed"}} ({{if index . "holds"}}holds{{else}}does not hold{{end}}){{end}}</td>
<td class="small">{{if .ApprovalID}}<a href="/approvals">approval</a> · <a href="/minds/{{$m.Slug}}/why?kind=approval&id={{.ApprovalID}}">why</a> {{end}}{{if .RequestID}}<a href="/approvals">request</a> {{end}}{{if .ExecutionID}}<a href="/minds/{{$m.Slug}}/why?kind=execution&id={{.ExecutionID}}">execution</a> {{end}}{{if .PredictionID}}<a href="/minds/{{$m.Slug}}/predictions">expectation</a> {{end}}{{if .IntentionID}}<a href="/minds/{{$m.Slug}}/intentions/{{.IntentionID}}">subordinate intention</a>{{end}}</td>
{{if $op}}<td>{{if eq .Status "blocked"}}<form method="post" action="/minds/{{$m.Slug}}/plans/{{$p.ID}}/steps/{{.Key}}/retry" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="btn small" data-confirm="Release this step? Check first that an interrupted action did not already run.">Release</button></form>{{end}}</td>{{end}}
</tr>{{end}}</tbody></table></section>
<section class="grid cols-2">
<div class="card"><h2>Resources</h2><table class="kv-table small"><thead><tr><th></th><th>Estimated</th><th>Used</th></tr></thead>
<tr><th>Capability invocations</th><td>{{$p.Estimate.Invocations}}</td><td>{{$p.Actual.Invocations}} of at most {{$p.Budget.MaxInvocations}}</td></tr>
<tr><th>Operator approvals</th><td>{{$p.Estimate.Approvals}}</td><td>{{$p.Actual.Approvals}}</td></tr>
<tr><th>Wall time (s)</th><td>{{printf "%.0f" $p.Estimate.WallSeconds}}</td><td>—</td></tr>
<tr><th>Network operations</th><td>{{$p.Estimate.Network}}</td><td>—</td></tr>
<tr><th>Money</th><td>{{printf "%.2f" $p.Estimate.Money}}</td><td>{{printf "%.2f" $p.Actual.Money}}</td></tr></table>
<h3>Completion criteria</h3><p class="small">{{$p.CompletionCriteria}}</p>
{{if $p.AbortConditions}}<h3>Abort conditions</h3><ul class="small">{{range $p.AbortConditions}}<li>{{.}}</li>{{end}}</ul>{{end}}
{{if $p.Assumptions}}<h3>Assumptions</h3><ul class="small">{{range $p.Assumptions}}<li>{{.Proposition}}{{if .BeliefID}} (<a href="/minds/{{$m.Slug}}/beliefs/{{.BeliefID}}">belief</a>, at least {{conf .MinConfidence}}){{end}}</li>{{end}}</ul>{{end}}
<h3>Risk</h3><p class="small">{{$p.Risk}}</p></div>
<div class="card"><h2>Alternatives considered</h2>
{{if $p.Alternatives}}<table class="table compact"><thead><tr><th>Strategy</th><th class="num">Expected confidence</th><th class="num">Approvals</th><th class="num">Score</th><th></th></tr></thead><tbody>{{range $p.Alternatives}}<tr><td>{{.Strategy}}<div class="small muted">{{trunc .Description 120}}</div></td><td class="num">{{conf .ExpectedConfidence}}</td><td class="num">{{.Estimate.Approvals}}</td><td class="num">{{printf "%.3f" .Score}}</td><td class="small">{{if .Selected}}<span class="badge ok">selected</span>{{end}} {{.Why}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None recorded.</p>{{end}}
<h3>Revisions</h3><ul class="plain small">{{range .Data.Revisions}}<li><a href="/minds/{{$m.Slug}}/plans/{{.ID}}">revision {{.Revision}}</a> · {{.Strategy}} · <span class="badge small">{{.Status}}</span> {{trunc .StatusReason 100}}</li>{{end}}</ul>
<h3>Validation</h3><pre class="json">{{.Data.Validation}}</pre></div>
</section>
{{end}}
+12
View File
@@ -0,0 +1,12 @@
{{define "content"}}
{{$m := .Data.Mind}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">plans</span></h1>
<p class="muted">Plans are revisable dependency graphs serving one intention. Go validates structure, the capability vocabulary and parameters; every action goes through policy; replans are new revisions and old plans stay as history.</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "plans")}}
{{template "volition_tabs" (dict "M" $m "Active" "plans")}}
<section class="card">
{{if .Data.Plans}}<table class="table compact"><thead><tr><th>Objective</th><th>Revision</th><th>Strategy</th><th>Origin</th><th>Status</th><th class="num">Invocations used</th><th class="num">Approvals</th><th>Updated</th></tr></thead><tbody>
{{range .Data.Plans}}<tr><td><a href="/minds/{{$m.Slug}}/plans/{{.ID}}">{{.Objective}}</a>{{with .StatusReason}}<div class="small muted">{{trunc . 140}}</div>{{end}}</td><td class="num">{{.Revision}}</td><td class="small">{{.Strategy}}</td><td class="small">{{.Origin}}</td><td><span class="badge">{{.Status}}</span></td><td class="num">{{.Actual.Invocations}} / {{.Budget.MaxInvocations}}</td><td class="num">{{.Actual.Approvals}}</td><td class="small muted nowrap">{{ago .UpdatedAt}}</td></tr>{{end}}</tbody></table>
{{else}}<p class="muted">No plans yet.</p>{{end}}
</section>
{{end}}
+40
View File
@@ -0,0 +1,40 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$a := .Data.Metrics}}{{$c := .Data.Calibration}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">cognitive research</span></h1>
<p class="disclaimer">{{.Data.Disclaimer}} Every figure below has an operational definition, shown beside it.</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "research")}}
{{template "volition_tabs" (dict "M" $m "Active" "research")}}
<section class="grid cols-2">
<div class="card"><h2>Volition and agency <span class="muted small">last 30 days</span></h2>
<table class="kv-table small">
<tr><th>Want → intention</th><td>{{pct $a.WantToIntention}} <span class="muted">of {{$a.WantsCreated}} wants</span></td></tr>
<tr><th>Intention → action</th><td>{{pct $a.IntentionToAction}} <span class="muted">of {{$a.IntentionsFormed}} intentions</span></td></tr>
<tr><th>Intention abandonment</th><td>{{pct $a.IntentionAbandonment}}</td></tr>
<tr><th>Blocked intentions</th><td>{{pct $a.BlockedIntentions}}</td></tr>
<tr><th>Commitment fulfilment</th><td>{{pct $a.CommitmentFulfilment}} <span class="muted">{{$a.CommitmentsFulfilled}} fulfilled, {{$a.CommitmentsBroken}} broken</span></td></tr>
<tr><th>Commitment conflicts</th><td>{{printf "%.2f" $a.CommitmentConflictPerItem}} per commitment</td></tr>
<tr><th>Replanning</th><td>{{pct $a.Replanning}} <span class="muted">of {{$a.PlansCreated}} plans</span></td></tr>
<tr><th>Outcome success</th><td>{{pct $a.OutcomeSuccess}}</td></tr>
<tr><th>Attribution of resolved expectations</th><td>{{range $k, $v := $a.Attribution}}{{$k}} {{$v}} · {{else}}none{{end}}</td></tr>
</table></div>
<div class="card"><h2>Introspection <span class="muted small">last 30 days</span></h2>
<p>Mean accuracy <strong>{{printf "%.2f" $a.IntrospectionAccuracy}}</strong> over {{$a.IntrospectionReplies}} replies · {{range $k, $v := .Data.IntrospectionLabels}}{{$k}} {{$v}} · {{end}}</p>
{{if .Data.Introspection}}<table class="table compact"><tbody>{{range .Data.Introspection}}<tr><td><span class="badge">{{.Label}}</span></td><td class="num">{{if .Value}}{{conf .Value}}{{end}}</td><td class="small">{{if .EventID}}<a href="/events/{{.EventID}}">reply</a>{{end}}</td><td class="small muted">{{ago .CreatedAt}}</td></tr>{{end}}</tbody></table>{{end}}
<h3>Action claims</h3>{{if .Data.Claims}}<ul class="plain small">{{range .Data.Claims}}<li><span class="badge">{{.Label}}</span> {{index .Detail "claim"}} <span class="muted">{{ago .CreatedAt}}</span></li>{{end}}</ul>{{else}}<p class="muted small">No first-person action claims recorded.</p>{{end}}
<h3>Self-model</h3>{{if .Data.SelfAccuracy}}<p>Capability self-model accuracy {{pct (deref .Data.SelfAccuracy)}}{{if .Data.SelfErrors}}: {{join .Data.SelfErrors "; "}}{{end}}</p>{{else}}<p class="muted small">The self-model lists no capabilities to compare.</p>{{end}}
</div>
</section>
<section class="card"><h2>Metacognitive calibration <span class="muted small">last 90 days, {{$c.Samples}} cited beliefs, revision window {{$c.RevisionWindowHours}} h</span></h2>
{{if $c.Samples}}<div class="bars">{{range $c.Buckets}}<span>{{printf "%.1f" .From}}–{{printf "%.1f" .To}}</span><span class="bar"><i style="width:{{pct .RevisedRate}}"></i></span><span>{{pct .RevisedRate}} revised of {{.Cited}}</span>{{end}}</div>
<p class="small">Brier score {{printf "%.3f" $c.Brier}} · after expressing uncertainty: {{pct $c.UncertainRevised}} revised ({{$c.UncertainSamples}}) · without: {{pct $c.ConfidentRevised}} ({{$c.ConfidentSamples}}){{if $c.ImprovedOverTime}} · first half {{printf "%.3f" $c.BrierFirstHalf}}, second half {{printf "%.3f" $c.BrierSecondHalf}} ({{if deref $c.ImprovedOverTime}}improved{{else}}not improved{{end}}){{end}}</p>
{{else}}<p class="muted">No replies cited beliefs in the period.</p>{{end}}
</section>
<section class="card"><h2>Experiments on this mind</h2>
{{if .Data.Experiments}}<table class="table compact"><thead><tr><th>Experiment</th><th>Protocol</th><th>Status</th><th class="num">Variants</th><th class="num">First divergence</th><th class="num">Architecture share</th><th>Reproducibility</th></tr></thead><tbody>
{{range .Data.Experiments}}<tr><td><a href="/experiments/{{.E.ID}}">{{.E.Name}}</a></td><td class="small">{{.Protocol}}</td><td><span class="badge">{{.E.Status}}</span></td><td class="num">{{.Variants}}</td><td class="num">{{if lt .First 0}}—{{else}}{{.First}}{{end}}</td><td class="num">{{printf "%.2f" .Share}}</td><td class="small">{{.Repro}}</td></tr>{{end}}</tbody></table>
{{else}}<p class="muted">None. Create one from a snapshot on the <a href="/experiments">experiments page</a>.</p>{{end}}
</section>
<section class="card"><h2>Operational definitions</h2>
{{range $s := .Data.Sections}}<h3>{{$s}}</h3><dl class="kv">{{range index $.Data.Measures $s}}<dt>{{.Title}} <span class="mono small">{{.Name}}</span></dt><dd class="small">{{.Definition}} <span class="muted">Not evidence of {{.NotEvidence}}.</span></dd>{{end}}</dl>{{end}}
</section>
{{end}}
+12
View File
@@ -0,0 +1,12 @@
{{define "content"}}
<div class="pagehead"><h1>Cognitive research</h1>
<p class="disclaimer">{{.Data.Disclaimer}}</p>
<p class="muted">Experiments fork a snapshot, feed identical stimuli to variants that differ in one respect, and measure the difference: whether entering the workspace changes downstream processing, what ablating a component does, whether the mind notices an inaccurate self-model, what would have happened without an event, and how much behaviour stays the same when the language model changes.</p></div>
<section class="card"><h2>Minds</h2><p>{{range .Data.Minds}}<a class="btn small" href="/minds/{{.Slug}}/research">{{.DisplayName}}</a> {{end}}</p></section>
{{range $p := .Data.Protocols}}{{with index $.Data.ByProtocol $p}}<section class="card"><h2>{{title $p}}</h2>
<table class="table compact"><thead><tr><th>Experiment</th><th>Hypothesis</th><th>Status</th><th class="num">Variants</th><th class="num">First divergence</th><th class="num">Architecture share</th><th>Reproducibility</th></tr></thead><tbody>
{{range .}}<tr><td><a href="/experiments/{{.E.ID}}">{{.E.Name}}</a></td><td class="small">{{trunc .E.Hypothesis 120}}</td><td><span class="badge">{{.E.Status}}</span></td><td class="num">{{.Variants}}</td><td class="num">{{if lt .First 0}}—{{else}}{{.First}}{{end}}</td><td class="num">{{printf "%.2f" .Share}}</td><td class="small">{{.Repro}}</td></tr>{{end}}</tbody></table></section>{{end}}{{end}}
<section class="card"><h2>Operational definitions</h2>
{{range $s := .Data.Sections}}<h3>{{$s}}</h3><dl class="kv">{{range index $.Data.Measures $s}}<dt>{{.Title}} <span class="mono small">{{.Name}}</span></dt><dd class="small">{{.Definition}} <span class="muted">Not evidence of {{.NotEvidence}}.</span></dd>{{end}}</dl>{{end}}
</section>
{{end}}
+25
View File
@@ -0,0 +1,25 @@
{{define "content"}}
{{$m := .Data.Mind}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">wants</span></h1>
<p class="muted">A want is a motivational preference with an origin and a strength that fades (half-life {{.Data.HalfLife}}). It is not a decision and never implies action; candidates below {{.Data.Threshold}} are not stored. Suppressed wants stay represented.</p></div></div>
{{template "mind_tabs" (dict "M" $m "Active" "wants")}}
{{template "volition_tabs" (dict "M" $m "Active" "wants")}}
<section class="card">
<h2>Active <span class="muted small">{{len .Data.Active}}</span></h2>
{{if .Data.Active}}<table class="table compact"><thead><tr><th>Want</th><th>Origin</th><th>Strength now</th><th class="num">Urgency</th><th class="num">Reinforced</th><th class="num">Set aside</th><th>Conflicts</th></tr></thead><tbody>
{{range .Data.Active}}<tr>
<td>{{if eq .Valence "avoid"}}<span class="badge warn">avoid</span> {{end}}<strong>{{.Description}}</strong>
<div class="small muted">{{with .Proposal}}{{if .Capability}}means: <code>{{.Capability}}</code> {{.Target}}{{else}}means: investigate {{.Subject}}{{end}} · {{end}}{{if .GoalID}}goal: <a href="/minds/{{$m.Slug}}/goals">{{index $.Data.Goals .GoalID}}</a> · {{end}}{{if .Drives}}drives {{join .Drives ", "}} · {{end}}{{if .Values}}values {{join .Values ", "}} · {{end}}{{if .OriginEvent}}<a href="/events/{{.OriginEvent}}">origin event</a>{{end}}</div></td>
<td class="small">{{.Origin}}</td>
<td><span class="conf"><span class="conf-bar"><i style="width:{{pct .Eff}}"></i></span> {{conf .Eff}}</span><div class="small muted">stored {{conf .Strength}}</div></td>
<td class="num">{{conf .Urgency}}</td><td class="num">{{.Reinforcements}}</td><td class="num">{{if .Suppressions}}<span class="danger">{{.Suppressions}}</span>{{else}}0{{end}}</td>
<td class="small">{{range .Conflicts}}<div>{{if .Preferred}}<span class="badge ok">preferred</span>{{else}}<span class="badge">other preferred</span>{{end}} vs “{{trunc .Other 60}}” <span class="muted">({{.Kind}}, {{.Outcome}})</span><div class="muted">{{.Rule}}</div></div>{{else}}<span class="muted">none</span>{{end}}</td>
</tr>{{end}}</tbody></table>
{{else}}<p class="muted">No active wants. Wants come from failed predictions, contradictions, operator goals, stalled goals, questions and requests, and due commitments.</p>{{end}}
</section>
<section class="card">
<h2>Closed <span class="muted small">{{len .Data.Closed}}</span></h2>
{{if .Data.Closed}}<table class="table compact"><thead><tr><th>Want</th><th>Status</th><th>Resolution</th><th>When</th></tr></thead><tbody>
{{range .Data.Closed}}<tr><td>{{.Description}}</td><td><span class="badge">{{.Status}}</span></td><td class="small">{{.Resolution}}</td><td class="small muted nowrap">{{if .ResolvedAt}}{{ago .ResolvedAt}}{{end}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">None.</p>{{end}}
</section>
{{end}}
+23
View File
@@ -0,0 +1,23 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$x := .Data.X}}
<div class="pagehead"><p class="muted small"><a href="/minds/{{$m.Slug}}/agency">agency</a></p><h1>Why did this happen?</h1>
<p class="muted">Constructed only from recorded state: rows and events. Nothing here is generated, and hidden model reasoning is not shown because it is not stored. Anything missing is listed as not recorded.</p></div>
{{template "mind_tabs" (dict "M" $m "Active" "agency")}}
<section class="card">
{{with $x.Action}}<div class="why-section"><h3>Action</h3><p><code>{{.Capability}}</code> on <span class="mono">{{.Target}}</span> — <span class="badge">{{.Status}}</span>{{if .DecidedBy}} · decided by {{.DecidedBy}}{{end}}{{with .Note}} (“{{.}}”){{end}}{{with .Result}}<br><span class="small muted">{{.}}</span>{{end}}</p></div>{{end}}
{{with $x.Step}}<div class="why-section"><h3>Immediate cause</h3><p>plan step <span class="mono">{{.Key}}</span> {{.Title}} ({{.Status}}){{with $x.Plan}} of <a href="/minds/{{$m.Slug}}/plans/{{.ID}}">{{.Text}}</a> <span class="small muted">{{.Detail}}</span>{{end}}</p></div>{{end}}
{{with $x.Intention}}<div class="why-section"><h3>Intention</h3><p><a href="/minds/{{$m.Slug}}/intentions/{{.ID}}">{{.Description}}</a> <span class="badge">{{.Status}}</span> <span class="small muted">{{.Kind}}, priority {{conf .Priority}}</span></p></div>{{end}}
{{with $x.Subordinate}}<div class="why-section"><h3>While waiting</h3><p>{{.Description}} <span class="badge small">{{.Status}}</span></p></div>{{end}}
{{if $x.Wants}}<div class="why-section"><h3>Originating wants</h3>{{range $x.Wants}}<p>{{.Description}} <span class="small muted">({{.Origin}}, strength {{conf .Strength}})</span></p>{{end}}</div>{{end}}
{{with $x.Goal}}<div class="why-section"><h3>Goal</h3><p>{{.Text}} <span class="small muted">{{.Detail}}</span></p></div>{{end}}
{{if $x.Beliefs}}<div class="why-section"><h3>Supporting beliefs</h3>{{range $x.Beliefs}}<p><a href="/minds/{{$m.Slug}}/beliefs/{{.ID}}">{{.Text}}</a> <span class="small muted">{{.Detail}}</span></p>{{end}}</div>{{end}}
{{if $x.Evidence}}<div class="why-section"><h3>Evidence</h3><ul class="plain small">{{range $x.Evidence}}<li>{{.Text}} <span class="muted">{{.Detail}}</span></li>{{end}}</ul></div>{{end}}
{{if $x.ConflictingWants}}<div class="why-section"><h3>Conflicting wants</h3>{{range $x.ConflictingWants}}<p>{{.Text}}<br><span class="small muted">{{.Detail}}</span></p>{{end}}</div>{{end}}
{{if $x.Commitments}}<div class="why-section"><h3>Commitments</h3>{{range $x.Commitments}}<p>{{.Polarity}}: {{.Description}}{{if .Unless}} unless {{.Unless}}{{end}} <span class="small muted">({{.Kind}})</span></p>{{end}}</div>{{end}}
{{if $x.Resolution}}<div class="why-section"><h3>Resolution</h3><ul class="small">{{range $x.Resolution}}<li>{{.}}</li>{{end}}</ul></div>{{end}}
{{with $x.Expectation}}<div class="why-section"><h3>Expected outcome</h3><p>{{.Text}} — <span class="badge">{{.Detail}}</span></p></div>{{end}}
{{with .Data.Attribution}}<div class="why-section"><h3>Attribution</h3><p><span class="badge">{{.Class}}</span> {{.Reason}}</p></div>{{end}}
{{if $x.Confidence}}<div class="why-section"><h3>Confidence</h3><p>{{conf $x.Confidence}} <span class="small muted">the intention's expected chance of achieving its outcome, as recorded when it formed</span></p></div>{{end}}
{{if $x.Gaps}}<div class="why-section"><h3>Not recorded</h3><ul class="small">{{range $x.Gaps}}<li>{{.}}</li>{{end}}</ul></div>{{end}}
</section>
{{end}}
+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 href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a><a href="/minds/{{$m.Slug}}/dreams" {{if eq $a "dreams"}}aria-current="page"{{end}}>Dreams</a><a href="/minds/{{$m.Slug}}/snapshots" {{if eq $a "snapshots"}}aria-current="page"{{end}}>Snapshots</a><a href="/minds/{{$m.Slug}}/replays" {{if eq $a "replays"}}aria-current="page"{{end}}>Replay</a><a href="/minds/{{$m.Slug}}/redactions" {{if eq $a "redactions"}}aria-current="page"{{end}}>Redactions</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 href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a><a href="/minds/{{$m.Slug}}/intentions" {{if or (eq $a "wants") (eq $a "intentions") (eq $a "commitments") (eq $a "plans") (eq $a "agency") (eq $a "research")}}aria-current="page"{{end}}>Volition</a><a href="/minds/{{$m.Slug}}/dreams" {{if eq $a "dreams"}}aria-current="page"{{end}}>Dreams</a><a href="/minds/{{$m.Slug}}/snapshots" {{if eq $a "snapshots"}}aria-current="page"{{end}}>Snapshots</a><a href="/minds/{{$m.Slug}}/replays" {{if eq $a "replays"}}aria-current="page"{{end}}>Replay</a><a href="/minds/{{$m.Slug}}/redactions" {{if eq $a "redactions"}}aria-current="page"{{end}}>Redactions</a>
</nav>
{{end}}
+13
View File
@@ -0,0 +1,13 @@
{{define "plan_dag"}}{{/* .Levels []dagLevel, .M mind */}}
<div class="dag" role="list" aria-label="Plan dependency graph">
{{range .Levels}}<div class="dag-level"><h4>{{if eq .Level 0}}first{{else}}after {{.Level}} step(s){{end}}</h4>
{{range .Steps}}<div class="dag-step st-{{.Status}}" role="listitem">
<span class="key">{{.Key}}</span> <span class="badge small">{{.Status}}</span><br>{{.Title}}
<div class="small muted">{{.Kind}}{{if .Capability}} · <code>{{.Capability}}</code>{{end}}{{if .DependsOn}} · after {{join .DependsOn ", "}}{{end}}</div>
{{with .Expectation}}<div class="small">expects: {{.Description}}</div>{{end}}
{{with .StatusReason}}<div class="small muted">{{trunc . 140}}</div>{{end}}
{{with .Error}}<div class="small danger">{{trunc . 140}}</div>{{end}}
</div>{{end}}
</div>{{end}}
</div>
{{end}}
+10
View File
@@ -0,0 +1,10 @@
{{define "volition_tabs"}}{{$m := .M}}{{$a := .Active}}
<nav class="tabs subtabs" aria-label="Volition and agency">
<a href="/minds/{{$m.Slug}}/wants" {{if eq $a "wants"}}aria-current="page"{{end}}>Wants</a>
<a href="/minds/{{$m.Slug}}/intentions" {{if eq $a "intentions"}}aria-current="page"{{end}}>Intentions</a>
<a href="/minds/{{$m.Slug}}/commitments" {{if eq $a "commitments"}}aria-current="page"{{end}}>Commitments</a>
<a href="/minds/{{$m.Slug}}/plans" {{if eq $a "plans"}}aria-current="page"{{end}}>Plans</a>
<a href="/minds/{{$m.Slug}}/agency" {{if eq $a "agency"}}aria-current="page"{{end}}>Agency timeline</a>
<a href="/minds/{{$m.Slug}}/research" {{if eq $a "research"}}aria-current="page"{{end}}>Research</a>
</nav>
{{end}}