query: stop asking the world about him

"во сколько у меня встреча" fell past the calendar, reached Kiwix, matched
an article on the 2015 CPISRA World Games and came back phrased as his
meeting. Inventing is worse than refusing, and as `system` this used to say
"пока не умею".

A new source sits between the notes pass and Kiwix: a question carrying a
possession marker ("у меня", "мой", "my", "do i have", "did i") that his own
data did not answer has no answer outside it either, so the walk stops there.
The markers are possession, not first person, so "как мне сварить борщ" still
reaches the encyclopedia.

This is also where CLAUDE.md's privacy line lands: only the utterance may
leave the box, and a question about him carries his life in the utterance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TrVSBKe3RFDF4fGYKWYQnX
This commit is contained in:
kami
2026-08-01 23:27:04 +04:00
parent b35151418a
commit b8227295b8
2 changed files with 174 additions and 0 deletions
+65
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"regexp"
"strings"
"time"
@@ -106,6 +107,11 @@ var querySources = []querySource{
{name: "embed", answer: (*reactiveHandler).queryEmbed},
{name: "memory", answer: (*reactiveHandler).queryMemory},
{name: "notes", answer: (*reactiveHandler).queryNotes},
// THE BOUNDARY. Everything above answers from his own data; everything
// below answers from the world's. A question about him that got this far
// has no answer in his data, and no outside source can supply one, so this
// stops the walk rather than let the encyclopedia and the model guess.
{name: "personal", answer: (*reactiveHandler).queryPersonal},
// The offline encyclopedia, after everything of his and before anything on
// the network. A question he can be answered from his own notes is answered
// from his own notes; only what is left over is looked up.
@@ -619,6 +625,65 @@ func (h *reactiveHandler) queryKiwix(ctx context.Context, t *queryTurn) (string,
return reply, true
}
// queryPersonal — stop the walk on a question about him that his own data did
// not answer.
//
// Every source above this one reads something of his: his facts, his calendar,
// his tasks, his house, his notes. Everything below reads the world: an offline
// Wikipedia, a page he named, the model's own weights. The world does not know
// when his meeting is, and asked anyway it will produce something.
//
// It did. "во сколько у меня встреча" reached Kiwix on the deployed daemon,
// 01-08-2026; Wikipedia matched an article on the 2015 CPISRA World Games, and
// the phraser rendered it as "встреча у тебя в 2015 CPISRA World Games, где
// были соревнования по плаванию". Fluent, confident, and about a swimming
// competition in Nottingham. Saying "не знаю" is not a worse answer than that
// one — it is the only true one.
//
// Note this is also the privacy edge. The rule in CLAUDE.md is that only the
// utterance may leave the box, never his notes; a question that is ABOUT him
// carries his life in the utterance itself, so it is the one class that should
// not be sent to an upstream engine at all. The guard closes both holes with
// the same test.
func (h *reactiveHandler) queryPersonal(ctx context.Context, t *queryTurn) (string, bool) {
if !isPersonalQuery(t.dec.Utterance) {
return "", false
}
log.Printf("voice: %q is about him and his own data did not answer it; not asking the world", t.dec.Utterance)
return "не знаю — не нашла у тебя такой записи.", true
}
// personalMarkers — first-person POSSESSION, not first person generally.
//
// "у меня" and "мой" attach to a thing that is his, which is what makes the
// question unanswerable from outside. A bare "мне" or "я" does not: "как мне
// сварить борщ" and "что я могу посмотреть" are ordinary questions about the
// world that happen to mention the asker, and refusing those would be the
// opposite mistake. The narrow test is the point.
// Go's \b is ASCII-only and never fires next to a Cyrillic letter, so the
// Russian patterns spell the boundary out as "not a letter or a digit". The
// English ones keep \b, where it works.
var personalMarkers = []*regexp.Regexp{
regexp.MustCompile(`(?i)(^|[^\p{L}\p{N}])у\s+меня([^\p{L}\p{N}]|$)`),
regexp.MustCompile(`(?i)(^|[^\p{L}\p{N}])мо(й|я|ё|е|и|его|ей|их|им|ими|ем|ю|ею)([^\p{L}\p{N}]|$)`),
regexp.MustCompile(`(?i)\bmy\b`),
regexp.MustCompile(`(?i)\bdo\s+i\s+have\b`),
regexp.MustCompile(`(?i)\bdid\s+i\b`),
}
// isPersonalQuery reports whether the utterance asks about something of his.
func isPersonalQuery(utterance string) bool {
if utterance == "" {
return false
}
for _, re := range personalMarkers {
if re.MatchString(utterance) {
return true
}
}
return false
}
// queryGeneral — general knowledge from the phraser, the last source before
// giving up. It always claims: either the model answers or Maven says she
// doesn't know.