the resolver ladder and the query chain report their claims (V-564)

The two claimant sets that live in the daemon are where the arbitration is
least visible: both are a hardcoded order of functions that each answer 'is
this mine?' alone. The ladder declares its roster up front, so a rung that
never ran is named rather than omitted, and the query chain does the same for
the sources below the one that claimed.

Recording is installed in runTurn and not in the IPC entry point, so the mic,
telegram and the web leave the same trail. A record only the web produced would
be missing exactly the turns that are hardest to reproduce.
This commit is contained in:
2026-08-06 00:52:39 +04:00
parent 5417692566
commit 5ac7347c38
4 changed files with 191 additions and 9 deletions
+18
View File
@@ -11,6 +11,7 @@ import (
"unicode"
"github.com/kami/maven/internal/crawl"
"github.com/kami/maven/internal/decision"
"github.com/kami/maven/internal/ipc"
"github.com/kami/maven/internal/memory"
"github.com/kami/maven/internal/morning"
@@ -158,8 +159,17 @@ var querySources = []querySource{
func (h *reactiveHandler) actionQuery(ctx context.Context, dec router.Decision) string {
t := &queryTurn{dec: dec}
// The roster, so the record can say which sources were never reached rather
// than leaving them out and letting a reader assume they looked and passed
// (V-564). Finish names everyone below the winner.
decision.Expect(ctx, decision.StageQuery, querySourceNames())
rec := decision.From(ctx)
for _, src := range querySources {
if dec.Continued && !src.dateAware {
rec.Note(decision.Claim{
Stage: decision.StageQuery, Claimant: src.name, Outcome: decision.NeverAsked,
Reason: "a continuation turn only asks the date-aware sources",
})
continue
}
if reply, ok := src.answer(h, ctx, t); ok {
@@ -172,8 +182,16 @@ func (h *reactiveHandler) actionQuery(ctx context.Context, dec router.Decision)
// caller asked for one, so /chat can show it (V-539).
log.Printf("voice: query claimed by source %q", src.name)
noteQuerySource(ctx, src.name)
rec.Note(decision.Claim{
Stage: decision.StageQuery, Claimant: src.name,
Intent: string(dec.Intent), Outcome: decision.Won,
})
return reply
}
rec.Note(decision.Claim{
Stage: decision.StageQuery, Claimant: src.name, Outcome: decision.Declined,
Reason: "it had no answer for this turn",
})
}
if dec.Continued {
// The previous question cannot be re-asked for another day. Saying so