First behavior-preserving slice of the Maven redesign. Establishes
explicit ingress/routing boundaries and enough observability to refactor
later without changing current routing, action, clarification, or
execution semantics.
Types introduced:
- NormalizedInput (internal/router/source.go): Text + InputSource,
the typed ingress boundary replacing raw string at the turn entry.
- InputSource (internal/router/source.go): channel provenance enum
(tap:voice, tap:text). Reuses the existing turnSource distinction.
- RouteProducer (internal/router/intent.go): which cascade stage
produced the decision (grammar, heads, llm, classifier).
Changes:
- Decision carries a Producer RouteProducer field, set at each cascade
stage (grammar, heads, LLM, classifier).
- turnRoute carries NormalizedInput instead of bare text string.
- runTurn takes NormalizedInput instead of (text, src).
- decision.Record carries InputSource and RouteProducer for
observability; RoutingTrace persists route_producer (migration #27).
- turnSource is now a type alias for router.InputSource.
Behavior preserved:
- Stage-0 grammars unchanged: same order, same matching, same confidence.
- Cascade fallthrough order unchanged (grammar → heads → llm → classifier).
- Clarification behavior unchanged.
- Action dispatch unchanged.
- No new linguistic normalization.
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>