-
-
Notifications
You must be signed in to change notification settings - Fork 7
Valence and Reflex
How memories carry feeling (
salience,emotion) and fire as reflexes (recall_mode), and how the curator completes the human-memory model.
Human memory doesn't treat every moment equally: emotionally charged moments consolidate harder and resurface faster, and some cues fire reflexively — no deliberate search involved. bastra-recall mirrors both axes (issue #217).
Two optional frontmatter fields carry the charge of the capture moment:
| Field | Values | Meaning |
|---|---|---|
salience |
0–1
|
How strongly the moment should be remembered. |
emotion |
frustration | success | risk | neutral
|
The tone of the moment. |
They are stamped by the capture rules — the same signals that already trigger a save now get stored:
- Recurring frustration ("schon wieder", emphatic caps) →
emotion: frustration,salience: 0.8 - A hard-won fix after >2 iterations →
emotion: success,salience: 0.7 - An explicit "remember this well" →
salience: 0.9
Routine saves carry neither field — the assistant never invents them. On overwrite without the fields, existing values are preserved (same rule as write_origin).
What valence does:
-
Slower forgetting — the effective expiration window is
days × (1 + salience): asalience: 1memory lives twice as long before aging to stale/expired. Emotional memories fade last. -
Ranking (shadow-first) — a bounded score multiplier (
1 + salience × cap, cap default0.25) runs in shadow mode: the would-be re-ranking is logged assalience_shadowon recall telemetry, but served results stay untouched.npm run salience-lift(eval workspace) joins the shadow logs with engagement episodes; only after a measured positive lift is the multiplier switched live viaBASTRA_SALIENCE_RANK=live. Same discipline as the trust multiplier (#160). - Visible feeling — in the Vault Map, salient memories glow: the aura scales with salience (size-independent, visible zoomed out) and takes the emotion's color — green (success), red (frustration), amber (risk).
recall_mode: reflex marks a memory as self-firing. On every non-trivial prompt, the prompt hook posts the text to POST /hook/reflex; the daemon hard-matches it against the recall_when phrases of reflex memories:
- Deterministic token-AND — every content token of a phrase (≥3 chars, function words stripped) must appear in the prompt. No fuzzy, no prefix, no scores.
-
Single-word triggers only for identifiers — a phrase reduced to one token fires only if it looks like an identifier (
npm-shrinkwrap,es2022), never for a plain word like "deployment" — that would be a scatter trigger. -
Strictly budgeted — max 2 injections per prompt (
BASTRA_REFLEX_MAX_PER_TURN, clamp 1–5), max once per memory per 4 h session window. Reflex hits bypass the empty-streak backoff (user-wired = never noise) and render as a dedicatedtrigger="reflex"hint block. -
Execution-traced — every firing is a
hook_reflextelemetry event (matched phrase, pool size, served ids).
Promotion is always your call. The assistant never sets recall_mode: reflex on its own. The curator watches usage: a memory that was acted on ≥3 times within 30 days becomes a candidate, relayed as a <reflex-candidate> block into your next session — the assistant asks, and only your explicit yes promotes it. Floors stay the manual pin; reflex is the rule-based, self-firing sibling.
-
Episodic → semantic — when ≥3 project-facts share a scope+topic and are all older than 30 days, the curator proposes a
<consolidation-candidate>: distill ONE lesson (linking the episodes via wikilinks) together with the user. The episodes stay; regular decay handles their fade — sleep-style consolidation, no LLM inside the daemon. -
Usage heat — the per-memory usage counters (#154) are joined into
GET /api/v1/graphas aheatvalue (0–1, log-scaled): often-applied memories carry a bright core in the map. Heat is the second demand clock next to valence.
| Env var | Default | Effect |
|---|---|---|
BASTRA_SALIENCE_RANK |
shadow |
off | shadow | live (case-insensitive) |
BASTRA_SALIENCE_RANK_CAP |
0.25 |
Max score boost: 1 + salience × cap
|
BASTRA_REFLEX |
on |
off disables the reflex lane |
BASTRA_REFLEX_MAX_PER_TURN |
2 |
Reflex injection budget per prompt (1–5) |
BASTRA_REFLEX_PROMOTION_MIN |
3 |
Acted-on recalls (30 d) before the curator proposes promotion |
Settings file: reflex: { enabled, maxPerTurn } in ~/.bastra/cli-settings.json (env wins).
Menschliches Gedächtnis behandelt nicht jeden Moment gleich: Emotional aufgeladene Momente konsolidieren stärker und tauchen schneller wieder auf, und manche Reize feuern reflexhaft — ganz ohne bewusste Suche. bastra-recall bildet beide Achsen ab (Issue #217).
Zwei optionale Frontmatter-Felder tragen die Ladung des Erfassungsmoments:
| Feld | Werte | Bedeutung |
|---|---|---|
salience |
0–1
|
Wie stark der Moment nachwirken soll. |
emotion |
frustration | success | risk | neutral
|
Der Ton des Moments. |
Gestempelt werden sie von den Capture-Regeln — dieselben Signale, die einen Save heute schon auslösen, werden jetzt gespeichert:
- Wiederkehrende Frustration („schon wieder", Caps) →
emotion: frustration,salience: 0.8 - Ein hart erkämpfter Fix nach >2 Iterationen →
emotion: success,salience: 0.7 - Ein explizites „merk dir das gut" →
salience: 0.9
Routine-Saves tragen keines der Felder — der Assistent erfindet sie nie. Bei Overwrite ohne die Felder bleiben Bestandswerte erhalten (gleiche Regel wie write_origin).
Was Valenz bewirkt:
-
Langsameres Vergessen — das effektive Ablauffenster ist
Tage × (1 + salience): ein Memory mitsalience: 1lebt doppelt so lang, bevor es zu stale/expired altert. Emotionale Erinnerungen verblassen zuletzt. -
Ranking (shadow-first) — ein begrenzter Score-Multiplikator (
1 + salience × cap, Cap default0.25) läuft im Shadow-Modus: Die Would-be-Reihenfolge wird alssalience_shadowin die Recall-Telemetrie geloggt, die servierten Ergebnisse bleiben unangetastet.npm run salience-lift(eval-Workspace) joint die Shadow-Logs mit Engagement-Episoden; erst nach gemessenem positivem Lift wird viaBASTRA_SALIENCE_RANK=livescharf geschaltet. Gleiche Disziplin wie beim Trust-Multiplier (#160). - Sichtbares Gefühl — in der Vault Map glühen saliente Memories: Die Aura skaliert mit der Salience (größenunabhängig, auch rausgezoomt sichtbar) und trägt die Emotionsfarbe — grün (success), rot (frustration), amber (risk).
recall_mode: reflex markiert ein Memory als selbst-feuernd. Bei jedem non-trivialen Prompt schickt der Prompt-Hook den Text an POST /hook/reflex; der Daemon matcht ihn hart gegen die recall_when-Phrasen der Reflex-Memories:
- Deterministisches Token-AND — jedes Inhaltstoken einer Phrase (≥3 Zeichen, Funktionswörter gefiltert) muss im Prompt vorkommen. Kein Fuzzy, kein Prefix, keine Scores.
-
Ein-Wort-Trigger nur für Identifier — eine auf ein Token reduzierte Phrase feuert nur, wenn sie wie ein Identifier aussieht (
npm-shrinkwrap,es2022), nie für ein Alltagswort wie „deployment" — das wäre ein Streutrigger. -
Streng budgetiert — max. 2 Injektionen pro Prompt (
BASTRA_REFLEX_MAX_PER_TURN, Clamp 1–5), max. 1× pro Memory pro 4-h-Session-Fenster. Reflex-Hits umgehen den Empty-Streak-Backoff (vom User verdrahtet = nie Noise) und erscheinen als eigenertrigger="reflex"-Hinweisblock. -
Execution-traced — jede Feuerung ist ein
hook_reflex-Telemetrie-Event (gematchte Phrase, Pool-Größe, servierte ids).
Promotion ist immer deine Entscheidung. Der Assistent setzt recall_mode: reflex nie eigenmächtig. Der Curator beobachtet die Nutzung: Ein Memory, das ≥3× in 30 Tagen angewandt wurde, wird Kandidat und landet als <reflex-candidate>-Block in deiner nächsten Session — der Assistent fragt, erst dein explizites Ja promotet. Floors bleiben der manuelle Pin; Reflex ist das regelbasierte, selbst-feuernde Geschwister.
-
Episodisch → semantisch — teilen ≥3 project-facts Scope+Topic und sind alle älter als 30 Tage, schlägt der Curator einen
<consolidation-candidate>vor: gemeinsam mit dem User EINE Lesson destillieren (Episoden per Wikilink verlinkt). Die Episoden bleiben; das reguläre Altern erledigt ihr Verblassen — Konsolidierung wie im Schlaf, ohne LLM im Daemon. -
Usage-Heat — die Nutzungszähler pro Memory (#154) fließen als
heat-Wert (0–1, log-skaliert) inGET /api/v1/graph: Oft angewandte Memories tragen in der Map einen hellen Kern. Heat ist die zweite Nachfrage-Uhr neben der Valenz.
| Env-Var | Default | Wirkung |
|---|---|---|
BASTRA_SALIENCE_RANK |
shadow |
off | shadow | live (case-insensitiv) |
BASTRA_SALIENCE_RANK_CAP |
0.25 |
Max. Score-Boost: 1 + salience × cap
|
BASTRA_REFLEX |
on |
off deaktiviert die Reflex-Lane |
BASTRA_REFLEX_MAX_PER_TURN |
2 |
Reflex-Budget pro Prompt (1–5) |
BASTRA_REFLEX_PROMOTION_MIN |
3 |
Acted-on-Recalls (30 T) bis der Curator eine Promotion vorschlägt |
Settings-Datei: reflex: { enabled, maxPerTurn } in ~/.bastra/cli-settings.json (Env gewinnt).