mavend: centralize action validation boundary (slice 4)

This commit is contained in:
2026-09-06 12:53:54 +04:00
parent 6a402bf556
commit 356766bce1
32 changed files with 2061 additions and 178 deletions
+12 -7
View File
@@ -19,6 +19,11 @@ func praxisActDec(fn string) router.Decision {
return router.Decision{Intent: router.IntentAct, Slots: router.Slots{Fn: fn, HasFn: true}}
}
// routeCandidate builds an ActionCandidate matching a route-resolved Decision.
func routeCandidate(fn string) router.ActionCandidate {
return router.ActionCandidate{Fn: fn, Source: router.ActionSourceRoute}
}
// praxisItemDec is praxisActDec for the lifecycle verbs, which need an item id
// in the value slot. Without one they answer "which item?" and never reach
// Praxis at all, which makes them useless for testing a Praxis outage.
@@ -46,7 +51,7 @@ func TestPraxisAttention_HappyPathSurfacesItems(t *testing.T) {
praxis := newFakePraxis(t, items)
h := newPraxisTestHandler(t, praxis)
reply := h.handlePraxisAct(ctx, praxisActDec("list_attention"))
reply := h.handlePraxisAct(ctx, praxisActDec("list_attention"), routeCandidate("list_attention"))
if !strings.Contains(reply, "disk almost full") {
t.Fatalf("expected attention digest to mention the item, got %q", reply)
}
@@ -77,7 +82,7 @@ func TestPraxisAttention_DegradedFailsClosedNotEmpty(t *testing.T) {
praxis.SetFault(500)
h := newPraxisTestHandler(t, praxis)
reply := h.handlePraxisAct(ctx, praxisActDec("list_attention"))
reply := h.handlePraxisAct(ctx, praxisActDec("list_attention"), routeCandidate("list_attention"))
if reply == "" {
t.Fatal("praxis outage must not produce an empty reply")
}
@@ -104,13 +109,13 @@ func TestFakeNexus_FaultInjectionThenRecovery(t *testing.T) {
}
nexus.SetFault(503)
reply := h.handleHexisAct(ctx, actDec("muzick indexer"))
reply := h.handleHexisAct(ctx, actDec("muzick indexer"), routeCandidate("restart"))
if actRan(reply) {
t.Fatalf("nexus outage must not report success, got %q", reply)
}
nexus.SetFault(0)
reply = h.handleHexisAct(ctx, actDec("muzick indexer"))
reply = h.handleHexisAct(ctx, actDec("muzick indexer"), routeCandidate("restart"))
if !actRan(reply) {
t.Fatalf("expected success once nexus recovers, got %q", reply)
}
@@ -134,7 +139,7 @@ func TestPraxisEntityAttention_RemembersWhatItReadOut(t *testing.T) {
reply := h.handlePraxisAct(ctx, router.Decision{
Intent: router.IntentAct,
Slots: router.Slots{Fn: "entity_attention", HasFn: true, Value: "muzick indexer"},
})
}, routeCandidate("entity_attention"))
if !strings.Contains(reply, "indexer wedged") {
t.Fatalf("expected the scoped item to be read out, got %q", reply)
}
@@ -147,7 +152,7 @@ func TestPraxisEntityAttention_RemembersWhatItReadOut(t *testing.T) {
}
// The follow-up resolves against what he just heard, not the stale list.
if reply := h.handlePraxisAct(ctx, praxisItemDec("resolve_item", "last")); reply == "" {
if reply := h.handlePraxisAct(ctx, praxisItemDec("resolve_item", "last"), routeCandidate("resolve_item")); reply == "" {
t.Fatal("positional follow-up should have been claimed by praxis")
}
var body string
@@ -175,7 +180,7 @@ func TestHexisConfirm_KeepsOneCorrelationIDPerAction(t *testing.T) {
hexis := newFakeHexis(t, caps, fixtureHexisExecuted("exec_1", "succeeded"))
h := ecoHandler(t, nexus, nil, hexis)
if reply := h.handleHexisAct(ctx, actDec("restart")); !strings.Contains(reply, "да") {
if reply := h.handleHexisAct(ctx, actDec("restart"), routeCandidate("restart")); !strings.Contains(reply, "да") {
t.Fatalf("mutating capability must ask for confirmation, got %q", reply)
}
resolve := findTrace(t, h, "nexus", "resolve")