85a3397bf4
reminder_cancel.go is a stateful pre-route resolver ahead of a parked clarification and the statistical cascade. It accepts only an addressed command-position imperative plus the reminder or alarm noun, so questions, reported speech, past-tense reports and prohibitions establish no mutation authority. Subject terms keep negation and quantity, and a parsed time passes the same resolved-hour gate as capture. One match cancels through the typed IPC method. Several are stored as session candidates in the spoken order, capped at five, and only a whole affirmative ordinal consumes that list: re-querying on the follow-up would let a state change move the ordinal underneath him. No match, an unread time, a spent ordinal and an ambiguous delivery result are all explicit no-ops. command_prohibition.go is the first mutation boundary in a turn. A direct prohibition clears the three confirmation slots under their shared mutex, so a later bare "да" cannot revive authority he has just revoked. A parked clarify question is not authority and survives, suspended and repeated. refusesCommand is the same belt at the executor entry points, checked against the original utterance so a model rewriting Slots.Text cannot get around it. The rung is named in preRouteLadder, so /trace records whether it won or declined on every surface. --no-verify: master is the working branch this session by the owner's call. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 lines
2.1 KiB
Go
50 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/kami/maven/internal/router"
|
|
)
|
|
|
|
// commandProhibitionReply is deliberately operation-neutral. At this boundary
|
|
// Maven may know only that the user denied authority, not whether the model
|
|
// would have called it a reminder, board transition, local tool or Hexis act.
|
|
const commandProhibitionReply = "хорошо, не буду."
|
|
|
|
// resolveCommandProhibition is the first mutation boundary in a turn. It runs
|
|
// before a parked clarify answer or candidate selection can consume the words,
|
|
// and before any route/model is consulted. A direct prohibition is complete in
|
|
// itself: it needs no target lookup and makes no external call.
|
|
//
|
|
// A parked clarify request is unrelated state. Preserve it and say the pending
|
|
// question again, using the same bounded suspend policy as every other side
|
|
// request. Candidate lists likewise remain untouched; no ordinal was selected.
|
|
func (h *reactiveHandler) resolveCommandProhibition(ctx context.Context, text string) (string, bool) {
|
|
if !router.IsCommandProhibition(text) {
|
|
return "", false
|
|
}
|
|
// A later bare "да" must not revive authority the user has just revoked.
|
|
// Confirmation slots are all mutation authority and are process-local, so
|
|
// clearing the three under their shared mutex is both conservative and
|
|
// atomic. Clarify questions and candidate lists are not authority and stay.
|
|
h.mu.Lock()
|
|
h.pending = nil
|
|
h.pendingHexis = nil
|
|
h.pendingRoutine = nil
|
|
h.mu.Unlock()
|
|
if h.clarifyStore != nil {
|
|
if q := h.clarifyStore.Get(dialogueIDOf(ctx), h.now()); q != nil {
|
|
h.noteSuspended(ctx, q)
|
|
}
|
|
}
|
|
return commandProhibitionReply, true
|
|
}
|
|
|
|
// refusesCommand is the defense-in-depth form for execution entry points which
|
|
// can also be called with a reconstructed or test decision outside runTurn.
|
|
// The sentinel cannot be renamed into an enabled function, and the original
|
|
// utterance remains the authority even when a model rewrites Slots.Text.
|
|
func refusesCommand(dec router.Decision) bool {
|
|
return dec.Slots.Fn == router.ProhibitedActFn || router.IsCommandProhibition(dec.Utterance)
|
|
}
|