router: add ActionCandidate type and ResolveActionCandidate

Introduce the typed boundary between routing and action resolution:

- ActionCandidate: Fn, Args, Source (route|matcher), Producer, Confidence
- ResolveActionCandidate(dec, m): standalone function usable by both
  the daemon and the eval harness
- Update eval harness Reach() to use ResolveActionCandidate instead of
  duplicating the matcher fallback logic

This is the routing-side half of the action-resolution boundary.
The daemon integration follows in the next commit.
This commit is contained in:
2026-09-05 21:49:25 +04:00
parent a55d90954a
commit 064747f192
3 changed files with 250 additions and 9 deletions
+4 -9
View File
@@ -121,7 +121,7 @@ var PraxisAliases = map[string]string{
// 1. A clarified act with a named entity target and no fn reaches Hexis before the clarify
// question is ever asked. That path runs on the raw slots, so the matcher
// does not get to fill fn first.
// 2. Otherwise the act matcher may earn a fn from the text slot.
// 2. Otherwise the act resolver produces an ActionCandidate from the route or matcher.
// 3. A fn that is a Praxis capability alias dispatches to Praxis.
// 4. An act with a named entity target reaches Hexis.
// 5. Anything else stays inside Maven.
@@ -137,14 +137,9 @@ func Reach(d router.Decision, m router.ActMatcher) (Service, string) {
}
return ServiceNone, ""
}
fn, hasFn := d.Slots.Fn, d.Slots.HasFn
if !hasFn && d.Slots.Text != "" && m != nil {
if matched, _, ok := m.Match(d.Slots.Text); ok {
fn, hasFn = matched, true
}
}
if hasFn {
if capability, ok := PraxisAliases[strings.ToLower(strings.TrimSpace(fn))]; ok {
candidate := router.ResolveActionCandidate(d, m)
if candidate.ActionResolved() {
if capability, ok := PraxisAliases[strings.ToLower(strings.TrimSpace(candidate.Fn))]; ok {
return ServicePraxis, capability
}
}