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
+23
View File
@@ -227,3 +227,26 @@ routing heads 93/96; destination was 11/33 and 25/33 respectively, and ecosystem
reach remained 28/30. The lifecycle reacquire test, focused race suite, full
aggregate command, and portable no-runtime packages all pass. Measurement:
`docs/evals/2026-08-13-onnx-runtime-lifecycle.md`.
### Clarification exhaustion is fail-closed
V-717 closes the terminal-policy hole found during the V-573 audit. A request
with two required gaps could spend its only question on the first, fill that
slot, and then reach `applyAction` with the second still absent. The attempt cap
was accidentally acting as permission to execute a partial action.
The resolver now rebuilds the pending action and re-runs the canonical
`missingFor` schema after every filled gap. One remaining gap produces exactly
one next question only while the shared `PendingAction.CanAsk` budget permits
it. Exhaustion visibly gives up, removes only the active stack level, and makes
no write or action. `finishRebuilt` repeats the same invariant at the execution
boundary. Reminder time answers remain separate from the clean payload but are
included in the schema decision used for validation.
The original `TestClarifySecondGapRespectsTheAttemptCap` now asserts the exact
give-up and zero reminders. New tests cover direct boundary refusal and a
two-level stack where exhausting the top appends the surviving lower question
to the same reply. The focused V-717 race cases pass in 4.529s; every clarify
case plus all 22 forced dialogue traces pass under the race detector in
26.202s; `internal/dialogue` passes under race in 2.293s. Routing contract:
`docs/routing.md` section “Required slots and attempt exhaustion”.