Files
Maven/cmd/mavend/command_prohibition_test.go
T
claude 0a2e194e76 router: migrate refusesCommand and ActHasEntityTarget to CapabilitySelection (slice 6c)
Migrate the two remaining action-routing consumers from compatibility
Decision.Slings fields to authoritative Decision.CapabilitySelection:

- refusesCommand: reads CapabilitySelection.Fn instead of Slots.Fn
- ActHasEntityTarget: reads CapabilitySelection.Resolved and
  CapabilitySelection.Args instead of Slots.HasFn and Slots.Args

Slots.Text remains the source for entity text when positional args do
not contain the target (unchanged).

Regression tests prove:
- prohibited sentinel preserved byte-for-byte through SelectCapability
- blanked Slots.Fn/Args/HasFn do not affect migrated consumers
- Praxis/Hexis entity-target routing unchanged
- stage-0 deterministic act unchanged
- classifier/extractor act unchanged

do not remove the compatibility mirrors yet.
2026-09-06 21:43:14 +04:00

80 lines
2.5 KiB
Go

package main
import (
"testing"
"github.com/kami/maven/internal/router"
)
// TestRefusesCommandUsesCapabilitySelection proves that refusesCommand reads
// CapabilitySelection.Fn rather than the compatibility Slots.Fn. When
// CapabilitySelection is populated with the prohibited sentinel but Slots.Fn
// is blank, the refusal must still fire.
func TestRefusesCommandUsesCapabilitySelection(t *testing.T) {
dec := router.Decision{
Intent: router.IntentAct,
CapabilitySelection: router.CapabilitySelection{
Fn: router.ProhibitedActFn,
Resolved: true,
},
// Slots compatibility fields deliberately blank.
}
if !refusesCommand(dec) {
t.Fatal("refusesCommand should return true when CapabilitySelection.Fn == ProhibitedActFn")
}
}
// TestRefusesCommandUtteranceFallback proves that the utterance-based
// prohibition check still works as a defense-in-depth belt when
// CapabilitySelection does not carry the sentinel.
func TestRefusesCommandUtteranceFallback(t *testing.T) {
dec := router.Decision{
Utterance: "don't restart nginx",
}
if !refusesCommand(dec) {
t.Fatal("refusesCommand should return true for a prohibited utterance")
}
}
// TestRefusesCommandNonProhibitedCapability proves that an act with a
// non-prohibited capability is NOT refused even when Slots.Fn happens to
// carry the prohibited sentinel (cross-contamination).
func TestRefusesCommandNonProhibitedCapability(t *testing.T) {
dec := router.Decision{
Intent: router.IntentAct,
Utterance: "restart nginx",
CapabilitySelection: router.CapabilitySelection{
Fn: "restart",
Resolved: true,
},
}
if refusesCommand(dec) {
t.Fatal("refusesCommand should return false for a non-prohibited capability")
}
}
// TestRefusesCommandProhibitedSentinelPreservedThroughPipeline proves that the
// command-prohibition grammar sentinel survives through SelectCapability into
// CapabilitySelection byte-for-byte.
func TestRefusesCommandProhibitedSentinelPreservedThroughPipeline(t *testing.T) {
dec := router.Decision{
Intent: router.IntentAct,
Slots: router.Slots{
Fn: router.ProhibitedActFn,
HasFn: true,
},
}
sel := router.SelectCapability(dec, nil)
dec.CapabilitySelection = sel
if dec.CapabilitySelection.Fn != router.ProhibitedActFn {
t.Errorf("CapabilitySelection.Fn = %q, want %q", dec.CapabilitySelection.Fn, router.ProhibitedActFn)
}
if !dec.CapabilitySelection.Resolved {
t.Error("CapabilitySelection.Resolved should be true")
}
if !refusesCommand(dec) {
t.Fatal("refusesCommand should return true after pipeline preserves the sentinel")
}
}