Query Praxis by canonical entity ref and back off enrichment retries #83
Closed
claude
wants to merge 1 commits from
overnight/eco-entity-refs into overnight/eco-degraded-suite
pull from: overnight/eco-entity-refs
merge into: kami:overnight/eco-degraded-suite
kami:master
kami:task/725-capability-ledger-and-empirical-baseline
kami:task/692-heads-path-may-equal-model-path-and-noth
kami:task/694-staticcheck-and-deadcode-are-still-not-i
kami:task/682-go-1-25-5-and-x-text-0-14-0-carry-20-rea
kami:task/674-caveats
kami:task/673-mavgpud-serves-the-model-to-the-whole-la
kami:task/487-capture-device-doc
kami:task/487-capture-device
kami:task/487-wake-word-deploy
kami:task/487-wake-word-threshold
kami:task/487-wake-word-stage-two
kami:task/671-mavwaked-registers-as-a-voice-consumer-i
kami:task/670-cut-claude-md-to-200-lines
kami:task/515-deploy-mavwaked-workpc
kami:task/669-prune-claude-md
kami:task/668-e4b-phrasing
kami:task/668-title-capital
kami:task/668-kiwix-answers-a-question-it-cannot-answe
kami:task/666-only-a-stage-0-grammar-may-take-the-pers
kami:task/487-mavwaked-has-no-wake-word-only-an-energy
kami:task/486-deploy-the-workstation-transcriber
kami:task/486-move-stt-and-tts-to-the-workstation-wher
kami:task/665-crisperwhisper-2-russian
kami:task/664-routing-heads-in-go
kami:task/662-usage-harness-source-badge
kami:task/661-post-merge-usage-rerun
kami:task/661-routing-heads-step-3-train-the-multi-hea
kami:task/660-router-prompt-destination
kami:task/659-destination-fixture
kami:task/655-query-source-is-a-routing-decision-made
kami:task/654-a-pending-clarify-has-no-way-out-neither
kami:task/654-week-of-usage-eval-docs
kami:task/649-needs-kami-telegram-is-the-only-reach-an
kami:task/643-memorystore-search-decodes-and-unmarshal
kami:task/641-two-maps-grow-for-the-process-lifetime-w
kami:task/644-mavcaldav-is-built-documented-as-running
kami:task/642-the-store-caps-sqlite-at-one-connection
kami:task/647-factenrichmentworker-walks-the-pending-q
kami:task/646-v-637-follow-up-telegram-intake-has-no-d
kami:task/638-no-deadline-survives-the-turn-path-from
kami:task/637-inbound-telegram-turns-and-corrections-f
kami:task/636-correcting-a-turn-from-telegram-and-from
kami:task/634-an-act-alias-resolves-the-verb-but-not-t
kami:task/630-one-gesture-correction-on-chat-v-628
kami:task/629-persist-the-routing-trace-and-record-it
kami:task/631-mode-inventory-written-from-the-handlers
kami:task/586-defaultfactparser-uses-hand-written-russ
kami:task/633-reconcile-the-seed-labels-with-the-handl
kami:task/627-reminder-verbs-has-no-alarm-verb-so-an-a
kami:task/626-the-classifier-seeds-teach-an-older-inte
kami:task/546-route-with-a-fine-tuned-e5-small-instead
kami:task/586-measure-the-fact-parser
kami:fix/gofmt-ecosystem-acts
kami:task/584-media-store-a-failed-write-leaks-its-bud
kami:task/518-no-write-path-for-a-backdated-event-so-t
kami:task/287-qa-voice-session-quality-polish
kami:task/492-qa-plan-reconcile
kami:task/530-sweep-tail-four-files-the-russian-sweep
kami:task/405-score-how-often-a-real-utterance-reaches
kami:task/529-money-and-list-pick-a-mechanism
kami:task/528-sweep-tail-the-three-files-on-467
kami:task/527-embedder-open-set-phrasings-stop-being-r
kami:task/526-morphology-a-dictionary-answers-the-gram
kami:task/525-lexicons-the-finite-russian-sets-move-to
kami:task/524-entity-reference-ask-nexus-about-every-l
kami:task/523-risk-tiers-take-hexis-s-tier-for-a-hexis
kami:task/521-review-pr-111-query-strings-declension-h
kami:task/491-llama-server-core-dumps-on-every-sigterm
kami:task/479-bug-an-unconfigured-capability-does-not
kami:task/467-bug-spoken-task-capture-is-dead-the-rout
kami:task/463-deploy-mavwaked-and-mavenclient-run-nowh
kami:task/480-hearing-no-shipped-client-can-start-a-re
kami:task/432-ambient-calendar-intake-is-fragile-and-p
kami:task/431-board-surface-maven-holds-the-work-board
kami:task/433-reactivehandler-has-30-fields-and-is-pas
kami:task/371-swap-the-embedder-for-an-asymmetric-retr
kami:task/408-review-31-07-split-the-30-method-coreapi
kami:task/410-review-31-07-hand-rolled-string-enums-st
kami:task/423-review-pr50-split-internal-ipc-server-go
kami:task/422-review-pr50-split-cmd-mavend-tick-go-860
kami:task/409-review-31-07-finish-moving-mavweb-markup
kami:task/482-ambient-ingest-reads-a-notification-s-ti
kami:task/444-kuma-a-fact-per-monitor-so-she-can-name
kami:task/452-capability-model-homelab-docker-restart
kami:task/449-destructive-confirm-policy-risk-tiers-no
kami:task/453-grocery-list-items-table-fourth-append-o
kami:task/399-run-the-persona-checks-inside-the-daemon
kami:task/448-bounded-follow-up-state-pending-candidat
kami:task/455-conversation-repair-name-the-misroute-co
kami:task/454-go-mod-tidy
kami:task/458-pronunciation-dictionary-for-piper
kami:task/456-command-history-read-only-query-over-exi
kami:task/457-clarification-templates-for-the-router-s
kami:task/474-query-source-ordering-feeds-and-calendar
kami:task/469-reminders-spelled-out-times-fail-the-bod
kami:task/475-bug-the-praxis-attention-capability-is-u
kami:task/481-bug-a-transient-complaint-is-stored-as-a
kami:task/476-bug-the-router-transliterates-latin-enti
kami:task/385-decide-whether-a-parked-clarify-question
kami:task/377-backfill-routines
kami:task/421-weather-geocoder
kami:task/390-no-read-path-for-delivery-attempts
kami:task/386-recall-fixture-filler-note-ids
kami:task/473-bug-morning-item-has-no-required-flag
kami:task/465-bug-make-simulate-routes-with-an-empty
kami:task/467-bug-spoken-task-capture-is-dead
kami:task/466-bug-a-pending-clarify-is-global-so-one-u
kami:task/468-bug-pattern-detect-has-no-minimum-interv
kami:task/462-bug-checkfeminine-flags-second-person-ma
kami:task/443-safekey-drops-cyrillic-so-russian-calend
kami:task/471-bug-agendaquerygrammars-covers-today-but
kami:task/383-slottext-in-clarify-answer-would-clobber
kami:task/323-qa-phraser-coverage-is-65-3-but-the-llam
kami:task/498-bug-and-x-reach-the-model-with-no-determ
kami:task/506-strings-family-6-summaries-and-reports-i
kami:task/504-strings-family-4-act-and-smart-home-repl
kami:task/503-strings-family-3-query-answers-and-gaps
kami:task/502-strings-family-2-capture-acknowledgement
kami:task/501-strings-family-1-phrasing-fallbacks-into
kami:task/397-phrasechat-and-phrasequery-hide-model-fa
kami:task/396-the-reply-path-can-t-be-tested-llmreplie
kami:task/496-recall-a-cross-language-question-loses-i
kami:task/495-bug-x-escapes-the-personal-boundary-and
kami:task/499-llama-server-holds-7-9gb-rss-for-a-1-1gb
kami:task/470-bug-a-question-writes-invented-knowledge
kami:task/493-bug-the-memory-index-stores-the-raw-utte
kami:task/490-name-the-gap-world-questions-through-the
kami:task/485-run-the-big-model-on-the-workstation-wit
kami:task/489-workstation-deploy-mavgpud-on-workpc-and
kami:task/488-workstation-a-supervisor-that-keeps-llam
kami:task/483-docs-offload-design
kami:task/483-design-offload-ml-to-the-workstation-kee
kami:task/459-docs-refresh-the-qa-plan-against-the-liv
kami:task/446-doc-reorg-tier-the-tree-retire-the-three
kami:fix/367-voice-parks-routine-accept
kami:task/365-dialogue-slots-and-router-slots-are-hand
kami:task/364-snooze-does-nothing-at-runtime-the-gate
kami:task/447-retire-progress-md-the-backlog-and-the-f
kami:task/445-session-workflow
kami:overnight/eco-versioned-traces
kami:overnight/eco-degraded-suite
kami:overnight/netscan
kami:overnight/smarthome
kami:overnight/replay-simulator
kami:overnight/event-envelope
kami:overnight/coldstart-unlock
kami:overnight/voice-barge-in
kami:overnight/stt-golden-audio
kami:overnight/senses-speaker
kami:overnight/senses-hearing
kami:overnight/senses-media-vision
kami:overnight/mcp-tools
kami:overnight/mcp-client
kami:overnight/self-update
kami:overnight/model-swap
kami:overnight/web-crawler
kami:overnight/rss-feeds
kami:overnight/email-poller
kami:overnight/email-extract
kami:overnight/email-imap
kami:overnight/money-zenmoney
kami:overnight/task-priority
kami:overnight/task-capture
kami:overnight/behavior-profile
kami:overnight/day-plan
kami:overnight/ambient-calendar
kami:overnight/local-calendar
kami:overnight/memory-eval
kami:overnight/proactive-proposals
kami:overnight/split-voice-quiet
kami:overnight/nginx-maven-block
kami:overnight/stepup-chat-surface
kami:integration/small-batch
kami:docs/fix-drift
kami:fix/ru-wording
kami:integration/jul31
kami:overnight/resident-1.7b
kami:overnight/nudge-templates
kami:overnight/kiwix-rewrite
kami:overnight/eval-writeup
kami:overnight/fix-truncation
kami:overnight/kiwix-client
kami:overnight/ru-prompts
kami:overnight/external-data
kami:overnight/phrasing-grammar
kami:overnight/talk-eval
kami:overnight/prompt-context
kami:overnight/prompt-address
kami:overnight/eval-label-kill
kami:overnight/delivery-boundary
kami:overnight/address-check
kami:overnight/system-replies-pr
kami:overnight/clock-intent-pr
kami:overnight/embedder-backfill-pr
kami:overnight/embedder-marker-pr
kami:overnight/note-recall-pr
kami:overnight/thinking-off-pr
kami:overnight/dialogue-persist-pr
kami:overnight/persona-2p-pr
kami:overnight/clarify-expiry-pr
kami:overnight/clarify-rework
kami:overnight/phrasing
kami:overnight/bakeoff
kami:overnight/recall-margin
kami:overnight/router-on
kami:overnight/slot-extract
kami:overnight/embedder-e5
kami:overnight/router-refusal
kami:overnight/eval-rerun
kami:overnight/eval-harnesses
kami:overnight/eval-rerun-base
kami:overnight/fmt-gate
kami:overnight/routines-fire
kami:overnight/router-prompt
kami:overnight/away-leak
kami:overnight/recall-eval
kami:overnight/snooze-works
kami:overnight/clarify-wiring
kami:overnight/delivery-tests
kami:overnight/routine-accept
kami:overnight/llm-router-flag
kami:overnight/clarify-data-layer
kami:overnight/loop-rule-tests
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "overnight/eco-entity-refs"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Maven's half of end-to-end entity propagation.
What changed
cmd/mavend/ecosystem_acts.go: newentityAttentionCapability(verbsentity_attention,что с,как дела у,статус). It resolves the subject through Nexus, then calls the already-existingpraxisClient.ListAttentionForEntityso the canonicalentity_idreaches Praxis as a query scope instead of being resolved and discarded. Spoken items are surfaced, not acknowledged. Facts already tagged with the sameentity_id(store.FactsByEntity) are folded into the same answer.cmd/mavend/factenrichment.go: per-fact exponential backoff (1 min doubling, capped at 1 h) after a transient resolve failure, plus anenrichmentStatusreport (pending, in-backoff, worst attempt count). Retry state is in memory on purpose — a restart legitimately retries everything. No fact is ever abandoned.cmd/mavend/entityrefs_test.go: fixtures for resolved, ambiguous, missing and delayed enrichment, plus backoff hold/release and cap.Out of scope (other repos)
Praxis observation intake still accepts
entity_refsand drops them, and Praxis has no active deferred-enrichment worker. Both are Praxis-side; recorded on the Vikunja task rather than reached into from here.Verified
make buildandmake testboth exit 0.Vikunja #272
The resolved
entity_idtravels to Praxis as a query scope. It is not resolved and then thrown away for a client-side filter. That is what makes the ref canonical. The refusals around it are right too. No Nexus means no scoped query at all, rather than an unscoped list relabelled as an answer about one entity. Ambiguity stops before Praxis is touched.TestEntityAttention_MissingAndDegradedAreDistinctpins the distinction that matters most, between "no such thing" and "I could not ask". Keeping the retry state in memory is the correct call. A restart legitimately retries everything, and the comment says so.1. A tick where the batch head is in backoff does no work at all.
PendingFactResolutionsisORDER BY id ASC LIMIT 20and knows nothing about backoff.due()filters afterwards. So the 20 oldest pending facts occupy the batch whether or not they are eligible. Walk it: Nexus is down for one tick, all 20 fail, each getsnextTry = now + 1m. Facts 21 through 500 are due and untouched. The next tick pulls the same 20 ids, skips all 20, and returns having resolved nothing. The backoff was meant to spare a struggling Nexus. It also stalls a healthy one behind facts that are not eligible yet. The sharp version is a poison subject, one that makes Nexus error every time. It is never given up on, by design. So it holds a batch slot forever, doing nothing for an hour at a time. Twenty of those and enrichment stops for good. No error, no log line, becausefailedis zero on a tick where everything was skipped. Either exclude backed-off ids in SQL, or keep pulling pages untilbatchdue facts are collected.2.
statushas no caller outside the test. The comment says degradation is reported and never hidden. It says a Nexus down all day must be visible as a backlog. Nothing readsenrichmentStatus. Not the dashboard, not/trace, not a log line, not the digest.ticklogs only whenfailed > 0, and per finding 1 that counter is zero in exactly the stalled state worth seeing. As written the backlog is visible toTestEnrichmentBackoff_HoldsAndReleasesand to nobody else. Onelog.Printfper tick whenPending > 0, or a row on/trace, would make the comment true.3.
InBackoffcannot exceed the batch size, andPendingcounts to 1000.w.skippedis set from the last tick, which iterated at most 20 facts.st.Pendingcomes from a fresh query with limit 1000. So a 500-fact backlog with every fact backed off reportsPending: 500, InBackoff: 20.MaxAttemptshas the same ceiling: it only sees facts that reached the head of a batch. The three numbers are not on the same denominator, and the field names do not say so.skippedis also stale between ticks, andintervalin deploy is not seconds, so the reported figure describes a window that has already passed.4. The scoped query fails open against a Praxis that does not know
entity_id.ListAttentionForEntityappends&entity_id=...and trusts the response. An older praxisd that ignores unknown query parameters returns the full unscoped list, and Maven reads it back asпо «Muzick indexer»: disk almost full. That is the exact outcomeTestEntityAttention_WithoutNexusSaysSoexists to prevent, arriving through a different door. The fake cannot catch it, becausenewFakePraxisserves the same fixture regardless of the query string. PR 82 builtfixtureNexusResolvedFlatfor precisely this class of "the other side is older than you think". The cheap guard is client-side. Drop any returned item whose ownentity_idis present and does not match. Refuse the answer when none of them carry one.Smaller:
resolveOnereturnsfalseafter thedelete(w.attempt, ...)has already run, soResolveFactEntityfailing gives no backoff at all. That path retries every tick with no ceiling. It is the one failure mode where the counter says something is wrong and the pacing does not apply.aliases()lists"что с","как дела у"and"статус", and dispatch isalias == dec.Slots.Fn.Fncarries a function slot from the act grammar, not free Russian. Those three are unreachable as written. Either the router emits them or they should go. An alias list that never matches claims coverage that is not there.attemptandnextTryare only cleaned on a successful resolve. A fact can leave the pending set another way, voided or resolved by a later write. Its entries then stay for the lifetime of the process.recordPraxisTrace(ctx, "entity_attention", ...)inherits theKind: "system"problem from PR 82, so the new trace is discarded like the others until 84 lands.localFactsForEntitytakes three facts and drops the rest with no marker. Reading three of eleven as "я помню: ..." presents a truncation as the whole of what she knows.Landed on master. The stack was one linear chain, so #84 carried every commit from #50 up, and master now contains this branch in full. Merging this PR on its own is an empty diff, so it is closed rather than merged. The review findings for it were fixed in the 2026-08-01 pass and are on master as commits on the stack tip, not on this branch.
Pull request closed