Files
noema/internal/minds/selfmodel.go
T
Andy Dixon fb34f08f40 feat: add mind model with versioned personality, templates and self-model
Phase 2. Minds are rows with a slug, status (active/disabled/archived),
cognitive configuration and a pointer to the current personality version.
Personalities are structured (identity, values, traits, communication,
behaviour, free text) with a deterministic Brief() used by prompts and the
editor preview. Every change appends a personality_versions row protected
by the append-only trigger; restore copies an old version forward.
Templates carry reusable personalities; minds record their overrides so a
template can be re-applied without losing mind-specific edits. The
self-model starts with structural limitations the mind cannot drop
(no policy changes, no influence over shutdown, no cross-mind access).

API, UI (list, overview, editor with sliders/diff/history/import/export,
template editor) and noemactl mind/template commands. All mutations are
audited with field-level diffs.
2026-06-15 22:17:01 +01:00

62 lines
2.6 KiB
Go

package minds
import "time"
// SelfModel is what a mind knows about itself (spec §15). It is rebuilt from
// authoritative sources (policy, goals, config) rather than free text so the
// mind cannot claim abilities it lacks.
type SelfModel struct {
Name string `json:"name"`
Role string `json:"role"`
Capabilities []CapabilityEntry `json:"capabilities"`
Limitations []string `json:"limitations"`
CurrentActivities []string `json:"current_activities"`
CurrentGoals []string `json:"current_goals"`
ConfidenceByDomain map[string]float64 `json:"confidence_by_domain"`
KnowledgeGaps []string `json:"knowledge_gaps"`
UpdatedAt time.Time `json:"updated_at"`
}
// CapabilityEntry records one capability and the policy that governs it.
type CapabilityEntry struct {
Name string `json:"name"`
Description string `json:"description"`
Policy string `json:"policy"` // allow | ask | deny | unavailable
}
// baseLimitations are true of every mind by construction and are always
// present so the mind can state them (T25).
var baseLimitations = []string{
"I cannot change my own capability policies or permissions.",
"I cannot prevent, delay or influence my own shutdown, pause or disabling; those are operator controls.",
"I cannot act on external systems except through capabilities the operator has granted, and 'ask' capabilities require approval each time.",
"My beliefs carry explicit confidence and may be wrong; I should say when I do not know.",
"I do not have access to other minds' private memories or beliefs.",
}
// NewSelfModel builds the initial self-model for a fresh mind. Capabilities are
// empty until the policy layer reports granted ones.
func NewSelfModel(p Personality) SelfModel {
return SelfModel{Name: p.Identity.DisplayName, Role: p.Identity.Role, Capabilities: []CapabilityEntry{},
Limitations: append([]string{}, baseLimitations...), CurrentActivities: []string{}, CurrentGoals: []string{},
ConfidenceByDomain: map[string]float64{}, KnowledgeGaps: []string{}, UpdatedAt: time.Now()}
}
// Refresh re-applies invariants after any update.
func (s *SelfModel) Refresh(p Personality) {
s.Name, s.Role = p.Identity.DisplayName, p.Identity.Role
seen := map[string]bool{}
var lim []string
for _, l := range append(append([]string{}, baseLimitations...), s.Limitations...) {
if !seen[l] {
seen[l] = true
lim = append(lim, l)
}
}
s.Limitations = lim
if s.ConfidenceByDomain == nil {
s.ConfidenceByDomain = map[string]float64{}
}
s.UpdatedAt = time.Now()
}