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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user