@@ -40,6 +40,7 @@ import { textSimilarity as snippetSimilarity } from "./memory/tokenize.js";
4040import {
4141 filterLiveShortTermRecallEntries ,
4242 readLightStagedKeys ,
43+ readPhaseSignalEntries ,
4344 readShortTermRecallEntries ,
4445 recordDreamingPhaseSignals ,
4546 recordRemConsideredPhaseSignals ,
@@ -1660,8 +1661,29 @@ async function runLightDreaming(params: {
16601661 lookbackDays : params . config . lookbackDays ,
16611662 } ) ,
16621663 } ) ;
1664+ // Filter entries already emitted in a prior light-phase cycle and not
1665+ // recalled again since. This prevents the work-summary block from
1666+ // repeating verbatim across consecutive cycles when the same entries
1667+ // remain within the lookback window. See #72096.
1668+ const phaseSignals = await readPhaseSignalEntries ( {
1669+ workspaceDir : params . workspaceDir ,
1670+ nowMs,
1671+ } ) ;
1672+ const freshEntries = recentEntries . filter ( ( entry ) => {
1673+ const signal = phaseSignals [ entry . key ] ;
1674+ if ( ! signal ?. lastLightAt ) {
1675+ return true ; // never emitted → include
1676+ }
1677+ const lastLightMs = Date . parse ( signal . lastLightAt ) ;
1678+ const lastRecalledMs = Date . parse ( entry . lastRecalledAt ) ;
1679+ if ( ! Number . isFinite ( lastLightMs ) || ! Number . isFinite ( lastRecalledMs ) ) {
1680+ return true ; // malformed timestamps → include conservatively
1681+ }
1682+ // Include only if the entry was recalled again after the last emission.
1683+ return lastRecalledMs > lastLightMs ;
1684+ } ) ;
16631685 const entries = dedupeEntries (
1664- recentEntries
1686+ freshEntries
16651687 . toSorted ( ( a , b ) => {
16661688 const byTime = Date . parse ( b . lastRecalledAt ) - Date . parse ( a . lastRecalledAt ) ;
16671689 if ( byTime !== 0 ) {
0 commit comments