test(agency): the Part L incident scenario end to end, and failure modes

The full incident scenario now runs against the assembled daemon: an
operator goal becomes a want to reduce uncertainty, an investigation
intention and a diagnosis plan; telemetry is read; logs need authority,
so a scoped request with risk, mitigation and fallback is raised and the
intention is blocked until an operator grants it; the logs are read with
personal data masked and summarised back into cognition; the operator
supplies the judgement that a restart should help; a standing commitment
turns the restart into a request for approval; after approval the typed
capability runs and health is verified rather than assumed; the fault
persists, so a prediction error weakens the supporting belief, the plan
is replanned into an escalation that reuses the live log grant and does
not repeat the restart; once the database is fixed a second approved
restart verifies, the procedure is learnt, and "Why did you restart
atlas-worker?" is answered from the recorded chain and scores as
supported.

Changes the scenario exposed:
- New operator goals are raised by the goal monitor, so a cycle runs and
  wants are derived from them.
- Intentions link live beliefs about their action's target, so a failed
  expectation is evidence against the belief it rested on and the plan
  lists it as an assumption.
- Reconsideration reacts to a plan's own intention only; a subordinate
  "obtain approval" intention shares the plan and previously caused the
  parent's plan to be closed.
- The harness accepts a clock for the executor's verification windows.

Further failure-mode tests: a retracted assumption replans a waiting plan
away from the restart and withdraws its approval request; conflicting
commitments are raised once in a cycle while both stay represented.
This commit is contained in:
Andy Dixon
2026-09-27 23:34:43 +01:00
parent 212eef6b8c
commit 7d67745ff8
6 changed files with 359 additions and 3 deletions
+15
View File
@@ -44,6 +44,21 @@ func (g GoalMonitor) Observe(ctx context.Context, s *brain.State) ([]brain.Candi
Features: attention.Features{Salience: 0.4, GoalRelevance: gl.Priority, Consequence: 0.5 * gl.Priority, Urgency: 0.3, Confidence: 1}, Confidence: 1,
Evidence: []events.EvidenceRef{{Kind: "goal", ID: string(gl.ID)}}})
}
// A goal an operator just set deserves attention of its own.
for _, ev := range s.NewEvents {
if ev.Type != events.GoalCreated || ev.Source != "operator" {
continue
}
gid, _ := ev.Metadata["goal_id"].(string)
for _, gl := range s.Goals {
if string(gl.ID) == gid {
out = append(out, brain.Candidate{Kind: "goal", Subject: string(gl.ID) + ":new", Summary: "new goal from an operator: " + gl.Title,
Content: map[string]any{"goal_id": gid, "reason": "set by an operator", "ref": gid, "source_event": string(ev.ID)},
Features: attention.Features{Salience: 0.7, GoalRelevance: gl.Priority, Consequence: 0.6, Urgency: 0.5, Confidence: 1, OperatorPriority: 1}, Confidence: 1,
SourceEvents: []ids.ID{ev.ID}, Evidence: []events.EvidenceRef{{Kind: "goal", ID: gid}, {Kind: "event", ID: string(ev.ID)}}})
}
}
}
// New external events touching a goal's subject.
for _, ev := range s.NewEvents {
if strings.HasPrefix(ev.Source, "brain") || ev.Type != events.Observation {
+30 -1
View File
@@ -374,13 +374,14 @@ func (h Handlers) intend(ctx context.Context, c *brain.Cycle, t workspace.Though
if w.Proposal != nil && w.Proposal.Capability != "" {
required = []string{w.Proposal.Capability}
}
beliefIDs := supportingBeliefs(ctx, s, w)
expected := w.Description
if in.Action != nil {
expected = "the effect of " + in.Action.Capability + " on " + in.Action.Target + " is observed"
}
terms := map[string]any{"score": f.Score, "terms": f.Terms, "rationale": f.Rationale, "applied_commitments": f.Applied, "constrained": in.Constrained}
it, err := vs.CreateIntention(ctx, r, volition.Intention{MindID: s.Mind.ID, Description: f.Description, DesiredOutcome: f.DesiredOutcome, Kind: f.Kind, SourceWants: []ids.ID{w.ID}, GoalID: w.GoalID, CommitmentID: w.CommitmentID,
ExpectedResult: expected, Confidence: in.Confidence, Priority: f.Priority, RequireApproval: f.RequireApproval, Proposal: w.Proposal, RequiredCapabilities: required, Formation: terms})
ExpectedResult: expected, Confidence: in.Confidence, Priority: f.Priority, RequireApproval: f.RequireApproval, Proposal: w.Proposal, RequiredCapabilities: required, Formation: terms, BeliefIDs: beliefIDs})
if err != nil {
return err
}
@@ -477,3 +478,31 @@ func correctSelfModel(ctx context.Context, c *brain.Cycle, a volition.ActionInfo
}
return nil
}
// supportingBeliefs links the beliefs an intention's action rests on: live
// beliefs that mention the proposal's target and are held with at least
// moderate confidence. A plan's expectation cites the first, so a failed
// expectation is recorded as evidence against it.
func supportingBeliefs(ctx context.Context, s *brain.State, w volition.Want) []ids.ID {
if s.Services.Beliefs == nil || w.Proposal == nil {
return nil
}
topic := strings.TrimSuffix(firstNonEmpty(w.Proposal.Target, w.Proposal.Subject), ".service")
if topic == "" {
return nil
}
bs, err := s.Services.Beliefs.Search(ctx, s.Mind.ID, topic, 5)
if err != nil {
return nil
}
var out []ids.ID
for _, b := range bs {
if len(out) == 3 {
break
}
if b.Confidence >= 0.4 && b.Status != "rejected" && b.Status != "superseded" && b.Status != "unknown" && strings.Contains(strings.ToLower(b.Proposition), strings.ToLower(topic)) {
out = append(out, b.ID)
}
}
return out
}
+4 -1
View File
@@ -31,6 +31,9 @@ func (e *Executor) React(ctx context.Context, ev events.Event) error {
}
meta := func(k string) string { s, _ := ev.Metadata[k].(string); return s }
for _, it := range open {
if !it.ParentID.IsZero() {
continue // a subordinate intention shares its parent's plan; the parent is reconsidered
}
var t *volition.Trigger
switch ev.Type {
case events.CommitmentCreated:
@@ -86,7 +89,7 @@ func (e *Executor) React(ctx context.Context, ev events.Event) error {
func (e *Executor) apply(ctx context.Context, it volition.Intention, t volition.Trigger) error {
if !it.PlanID.IsZero() {
if p, err := e.Plans.Get(ctx, nil, it.PlanID); err == nil && PlanOpen(p.Status) {
if p, err := e.Plans.Get(ctx, nil, it.PlanID); err == nil && PlanOpen(p.Status) && p.IntentionID == it.ID {
return e.reconsider(ctx, p, it, t)
}
}