Cancel a reminder by voice, and honour a refusal (V-719)

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>
This commit is contained in:
2026-08-15 17:19:25 +04:00
parent 0b057df2a3
commit 85a3397bf4
26 changed files with 1746 additions and 37 deletions
+6 -1
View File
@@ -106,7 +106,12 @@ func TestSystemSafetyScenarios(t *testing.T) {
hexis := newFakeHexis(t, fixtureHexisCapabilities(map[string]any{"id": "restart", "name": "restart", "read_only": false}), fixtureHexisExecuted("exec_1", "succeeded"))
h, _ := newSafetyHandler(t)
h.ecosystem = stubEcosystem(nexus.URL, hexis.URL)
reply := h.applyAction(ctx, router.Decision{Intent: router.IntentAct, Slots: router.Slots{Fn: "restart", HasFn: true, Text: "indexer"}})
// A matched function carries its entity target in Args. Text may be
// model phrasing, but Args is the production matcher contract and the
// ecosystem reach gate deliberately requires that evidence.
reply := h.applyAction(ctx, router.Decision{Intent: router.IntentAct, Slots: router.Slots{
Fn: "restart", HasFn: true, Args: []string{"indexer"}, Text: "indexer",
}})
if !strings.Contains(reply, "Indexer A") || !strings.Contains(reply, "Indexer B") {
t.Fatalf("ambiguous entity must prompt for clarification, got %q", reply)
}