Give up instead of acting on a missing slot (V-717)

The clarification attempt cap bounded questions, not the action schema. A
request with two required gaps could spend its budget on the first, fill it,
and reach applyAction with the second still absent, so the cap acted as
permission to execute a partial action.

resolveClarifyAnswer now rebuilds the pending action and re-runs the canonical
missingFor check after every filled gap. One remaining gap yields exactly one
next question while PendingAction.CanAsk permits it. Exhaustion says the
give-up line, pops only the active stack level, and performs no write or
action. finishRebuilt repeats the invariant at the execution boundary, so a
future dialogue caller cannot bypass it. Reminder time answers stay out of the
spoken payload but ride along in the decision copy used for validation.
This commit is contained in:
2026-08-13 11:33:05 +04:00
parent 06576b406c
commit 8ee3b76af6
4 changed files with 154 additions and 35 deletions
+20
View File
@@ -338,6 +338,26 @@ tests are offline and cost nothing. False clarifies 3 to 2, intent-only 74.0% to
The two remaining false clarifies are the act-with-no-allowlisted-fn arm of the
gate, not this rule.
### Required slots and attempt exhaustion
A clarification budget limits questions, not the action schema (V-717). The
same `missingFor` check used on a fresh route is applied after each answer to a
parked request. If another required slot remains and `PendingAction.CanAsk`
allows it, Maven asks exactly that first gap and re-parks the request. If the
budget is spent—or that gap has no valid question—Maven says the give-up line,
pops the active request, and performs no write or action. There is no
"best-effort" partially filled execution.
Reminder time answers are held separately from the utterance that becomes the
spoken payload. The schema check therefore receives a decision copy carrying
the full time evidence, while the action receives the clean payload decision.
The rebuilt-action boundary repeats the `missingFor` invariant before
`applyAction`, so a future dialogue caller cannot bypass it.
At stack depth two, exhausting the active request uses `CompleteTop`: the lower
request remains parked, its answer window restarts, and its one resumed question
is appended after the visible give-up in the same reply.
## The destination
`query` was a shrug. The cascade sorted an utterance into one of seven intents,