router: introduce typed ActionValidationStatus boundary (slice 5)

Introduce ActionValidationStatus enum (valid, unresolved, missing_argument,
invalid_argument, ambiguous_target) as the typed classification of validation
outcomes. ActionValidationResult now carries Status instead of boolean flags.

Backward-compatible: Unresolved() and Valid() methods preserved on the result.
Existing validation behavior unchanged: only blank Fn produces invalid_argument.
All downstream behavior (proposeGap, confirmation, task_status, praxis, hexis)
unchanged.

Tests added for all five status values, backward compatibility, and the full
validation → execution boundary.
This commit is contained in:
2026-09-06 13:07:08 +04:00
parent 356766bce1
commit 66c578a6f4
5 changed files with 357 additions and 39 deletions
+8 -7
View File
@@ -54,22 +54,23 @@ func noteActionResolution(ctx context.Context, source, fn string, resolved bool)
}
// noteActionValidation records the structural validation outcome in the
// decision trace. Three outcomes: unresolved (matcher miss), valid
// (structurally admissible), or invalid (structurally malformed).
// 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 {
case v.Unresolved:
switch v.Status {
case router.ActionUnresolved:
rec.Note(decision.Claim{
Stage: decision.StageAction,
Claimant: "action-validation",
Outcome: decision.Declined,
Reason: "unresolved",
})
case v.Valid:
case router.ActionValid:
rec.Note(decision.Claim{
Stage: decision.StageAction,
Claimant: "action-validation",
@@ -77,9 +78,9 @@ func noteActionValidation(ctx context.Context, v router.ActionValidationResult)
Reason: "valid",
})
default:
reason := "invalid"
reason := string(v.Status)
if len(v.Issues) > 0 {
reason = "invalid:" + v.Issues[0].Reason
reason = string(v.Status) + ":" + v.Issues[0].Reason
}
rec.Note(decision.Claim{
Stage: decision.StageAction,