@@ -122,6 +122,7 @@ def __init__(
122122 decay_enabled : bool = False ,
123123 decay_half_life_days : float = 30.0 ,
124124 decay_weight : float = 0.5 ,
125+ decay_kinds : frozenset [MemoryKind ] | None = None ,
125126 ) -> None :
126127 self .db_path = db_path
127128 self .dimensions = dimensions
@@ -152,11 +153,21 @@ def __init__(
152153 # Time-aware boost over the final note ranking. Off by default
153154 # (opt-in via settings) so existing behaviour is unchanged.
154155 # ``decay_weight`` caps the multiplicative boost; setting it to
155- # 0 disables decay even when ``decay_enabled`` is True. See
156- # :mod:`marvin.decay` for the rationale.
156+ # 0 disables decay even when ``decay_enabled`` is True.
157+ # ``decay_kinds`` restricts the boost to specific note kinds --
158+ # default is EPISODIC only because semantic, procedural, and
159+ # reflective notes encode (mostly) timeless content where
160+ # "fresher = better" doesn't hold. Pass ``frozenset(MemoryKind)``
161+ # to apply decay to every kind. See :mod:`marvin.decay` for the
162+ # rationale.
157163 self .decay_enabled = decay_enabled
158164 self .decay_half_life_days = max (1e-3 , decay_half_life_days )
159165 self .decay_weight = max (0.0 , decay_weight )
166+ self .decay_kinds : frozenset [MemoryKind ] = (
167+ decay_kinds
168+ if decay_kinds is not None
169+ else frozenset ({MemoryKind .EPISODIC })
170+ )
160171 self .db_path .parent .mkdir (parents = True , exist_ok = True )
161172 self .conn = sqlite3 .connect (self .db_path )
162173 self .conn .row_factory = sqlite3 .Row
@@ -531,9 +542,10 @@ def _apply_decay_boost(
531542 ) -> None :
532543 """Mutate ``scores`` in place with a freshness multiplier.
533544
534- Pulls ``updated_at`` for the candidate paths in a single query
535- rather than threading the timestamp through every row in every
536- first-stage stream. Notes whose ``updated_at`` cannot be parsed
545+ Pulls ``updated_at`` and ``kind`` for the candidate paths in a
546+ single query rather than threading the timestamp through every
547+ row in every first-stage stream. Notes whose ``updated_at``
548+ cannot be parsed, or whose kind isn't in :attr:`decay_kinds`,
537549 are left untouched (multiplier = 1.0), matching the no-op
538550 contract of :func:`marvin.decay.freshness_boost`.
539551 """
@@ -542,11 +554,18 @@ def _apply_decay_boost(
542554 paths = list (scores .keys ())
543555 placeholders = "," .join ("?" for _ in paths )
544556 rows = self .conn .execute (
545- f"SELECT relative_path, updated_at FROM notes "
557+ f"SELECT relative_path, kind, updated_at FROM notes "
546558 f"WHERE relative_path IN ({ placeholders } )" ,
547559 paths ,
548560 ).fetchall ()
561+ kinds = self .decay_kinds
549562 for row in rows :
563+ try :
564+ kind = MemoryKind (row ["kind" ])
565+ except ValueError :
566+ continue
567+ if kind not in kinds :
568+ continue
550569 note_time = parse_note_timestamp (row ["updated_at" ])
551570 if note_time is None :
552571 continue
0 commit comments