mavend: run the persona checks over the clarify prose, document the proposal cooldown

clarifyExpiredVariants and clarifyGaveUp are hand-written Russian that the
phrasing eval never sees, because they never pass through the phraser. They
carry feminine self-reference and a plain imperative, and they are the lines a
later edit reaches for a synonym in. A table test now runs the eval's own
feminine, his-gender, address and cringe checks over them and over
clarifyQuestions. The apology clause of the cringe check is skipped with its
reason written down: it exists so a greenlit nudge is not undercut, and a reply
to a request she failed to parse is the opposite case.

Also two notes and no behaviour change. announceProposal now says what its
cooldown does and does not do: detectAndPropose returns non-nil only for a newly
created row, so the first tick over a populated history announces one pattern
and silences the rest permanently, and the cooldown only spaces genuinely new
pairs found later. A queue would be needed for "one per day until each is
mentioned". The duplicated Cooldown default is explained as cover for a tickLoop
built in a test without going through Load. The -reembed flag help says the
daemon does not answer until the backfill finishes.

Found in review of #50, #54.

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 14:05:59 +04:00
parent 4757ff6d7b
commit fa799bc051
3 changed files with 59 additions and 1 deletions
+17
View File
@@ -417,6 +417,8 @@ func (t *tickLoop) detectPatterns(ctx context.Context, now time.Time, state loop
log.Printf("tick: proposed routine: %s/%s every %.1f days", r.Action, r.Object, r.IntervalDays)
// One announcement per tick at most, whatever the scan turned up. The
// rest are on /routines; they are not lost, they are just not shouted.
// Nor are they queued: the row now exists, so no later tick re-detects
// them and they are never announced. See announceProposal.
if announced {
continue
}
@@ -437,6 +439,21 @@ func (t *tickLoop) detectPatterns(ctx context.Context, now time.Time, state loop
// re-detect it and nothing queues up behind it. A missed announcement means
// he reads it on /routines instead, which is the whole point of the page.
//
// What the cooldown is and is not. detectAndPropose returns non-nil only for a
// newly created row, so a pair gets exactly one chance to be spoken: the tick
// that first proposes it. Combined with one announcement per tick, the first
// tick over a populated history announces one pattern and permanently silences
// every other pattern found in the same pass. That is the intent, not an
// oversight — an inferred routine is not worth a second attempt at his
// attention, and /routines lists all of them. So the cooldown does not drain a
// backlog. It only spaces announcements of genuinely new pairs discovered on
// later ticks. If it should ever become "one per day until each is mentioned",
// that needs a queue rather than this counter.
//
// Cooldown gets its default here as well as in applyDefaults. That is
// deliberate: a tickLoop assembled directly in a test never goes through Load,
// and an unspaced announcer is not what those tests mean to exercise.
//
// The body is the detector's own literal Russian phrasing (pattern.PhraseRoutine
// — "ты заправляешь поилку раз в 7 дней — напоминать?"), not LLM-generated, so
// an inferred routine cannot arrive worded as something Maven never observed.