package main import ( "context" "github.com/kami/maven/internal/decision" "github.com/kami/maven/internal/router" ) // resolveAction produces an ActionCandidate from a routing decision. It is the // single boundary between routing and action execution: everything downstream // (refusesCommand, task-status, Praxis, Hexis, proposeGap, tool.Executor.Exec) // consumes the candidate rather than re-resolving the function. // // Delegates to router.ResolveActionCandidate for the resolution logic, then // records the outcome in the decision trace. func (h *reactiveHandler) resolveAction(ctx context.Context, dec router.Decision) router.ActionCandidate { candidate := router.ResolveActionCandidate(dec, h.matcher) // Record the resolution outcome in the decision trace. if dec.Intent == router.IntentAct { if candidate.ActionResolved() { noteActionResolution(ctx, string(candidate.Source), candidate.Fn, true) } else { noteActionResolution(ctx, "matcher", "", false) } } return candidate } // noteActionResolution records the action resolution outcome in the decision // trace. A nil recorder is the normal case in tests. func noteActionResolution(ctx context.Context, source, fn string, resolved bool) { rec := decision.From(ctx) if rec == nil { return } outcome := decision.Declined reason := "no match" if resolved { outcome = decision.Won reason = "resolved via " + source if fn != "" { reason += ": " + fn } } rec.Note(decision.Claim{ Stage: decision.StageAction, Claimant: "action-resolve", Outcome: outcome, Reason: reason, }) } // noteActionValidation records the structural validation outcome in the // decision trace. Five outcomes: unresolved (matcher miss), valid // (structurally admissible), invalid_argument, missing_argument, or // ambiguous_target (structurally malformed). func noteActionValidation(ctx context.Context, v router.ActionValidationResult) { rec := decision.From(ctx) if rec == nil { return } switch v.Status { case router.ActionUnresolved: rec.Note(decision.Claim{ Stage: decision.StageAction, Claimant: "action-validation", Outcome: decision.Declined, Reason: "unresolved", }) case router.ActionValid: rec.Note(decision.Claim{ Stage: decision.StageAction, Claimant: "action-validation", Outcome: decision.Won, Reason: "valid", }) default: reason := string(v.Status) if len(v.Issues) > 0 { reason = string(v.Status) + ":" + v.Issues[0].Reason } rec.Note(decision.Claim{ Stage: decision.StageAction, Claimant: "action-validation", Outcome: decision.Declined, Reason: reason, }) } }