diff --git a/scripts/dream_observation.py b/scripts/dream_observation.py index 9ba7ff4..a7e394f 100644 --- a/scripts/dream_observation.py +++ b/scripts/dream_observation.py @@ -186,50 +186,38 @@ def observe_corpus(): # ─── Stage 2: select_mode ─────────────────────────────────────────────────── def select_mode(signal, task=None, explicit_mode=None): - """Return one of {'nrem', 'early-rem', 'late-rem', 'lucid'} or None. + """Return one of {'nrem', 'early-rem', 'late-rem', 'lucid'}. Never None. - Selection logic from spec (lines 69–74): - - Explicit mode argument → use that mode + The dreamer fires every scheduled night. The earlier "go quiet on null + delta" rule was a synthesis-doc invention that didn't match the actual + desired UX — the original dreamer always dreamed, even if it repeated + itself. The cure for repetition lives in the retrieve layer + (LLM-generated queries from the observation signal, MMR diversity, + cursor bias toward under-processed chunks), not in skipping nights. + + Routing logic: + - explicit_mode argument wins - task supplied → 'lucid' (question-anchored) - - Active journal entry → Early REM (emotional/personal register) - - Corpus unchanged ≥ STALENESS_TRIGGER_DAYS → Late REM (shake loose) - - New chunks above threshold → NREM - - Otherwise → None ("dreamer goes quiet rather than manufacturing novelty") - - The None return is load-bearing. Per spec line 67, it's the canonical - answer to the repetition problem: when nothing has changed, do not - manufacture a dream just to fill the schedule slot. + - days_since_dream ≥ STALENESS_TRIGGER_DAYS → 'late-rem' (shake loose + via cross-domain pairs when nothing's been added in a while) + - new journal entry → 'early-rem' (emotional/personal register) + - default → 'nrem' (replay-and-consolidation; always has something to + do because the corpus always has under-processed chunks) """ if explicit_mode: return explicit_mode if task: return "lucid" - new_chunks = signal["new_chunks"] - new_journal = signal["new_journal_entries"] days_since = signal["days_since_dream"] + new_journal = signal["new_journal_entries"] - # Spec line 72: corpus unchanged ≥3 days → Late REM ("shake things loose"). - # This rule has to win against the "go quiet" rule below — the spec uses - # quiet as the default for 1-2 days of silence, then deliberately fires - # Late REM at 3+ days to break the stasis. Checking staleness first is - # what implements that intent. (Previous version checked "go quiet" first - # and never reached this branch.) if days_since >= STALENESS_TRIGGER_DAYS: return "late-rem" - # Spec line 71: journal entry → Early REM - # We treat "any new journal entry" as the trigger. Refining to detect - # emotional/personal register requires LLM sentiment tagging, which - # is left for a later iteration. if new_journal: return "early-rem" - # Spec line 67: nothing changed within the staleness window → quiet - if new_chunks < NEW_CHUNK_THRESHOLD and not new_journal: - return None - - # Default: new chunks above threshold → NREM return "nrem"