@@ -1118,6 +1118,147 @@ describe("short-term promotion", () => {
11181118 ) . toBe ( true ) ;
11191119 } ) ;
11201120
1121+ it ( "treats snippets with metadata prefix before the Candidate marker as contaminated" , ( ) => {
1122+ expect (
1123+ __testing . isContaminatedDreamingSnippet (
1124+ "- - status: staged - Candidate: User: [cron:26fb656d] run thing - confidence: 0.00 - evidence: memory/.dreams/session-corpus/2026-04-12.txt:25-25 - recalls: 0 - status: staged" ,
1125+ ) ,
1126+ ) . toBe ( true ) ;
1127+ } ) ;
1128+
1129+ it ( "treats snippets with confidence prefix before the Candidate marker as contaminated" , ( ) => {
1130+ expect (
1131+ __testing . isContaminatedDreamingSnippet (
1132+ "confidence: 0.58 - Candidate: Assistant: Mason shipped the enforcement pass. - evidence: memory/.dreams/session-corpus/2026-04-11.txt:167-167 - recalls: 0 - status: staged" ,
1133+ ) ,
1134+ ) . toBe ( true ) ;
1135+ } ) ;
1136+
1137+ it ( "does not treat prose that mentions the word Candidate as contaminated" , ( ) => {
1138+ expect (
1139+ __testing . isContaminatedDreamingSnippet (
1140+ "The Candidate profile for Josh Rhoden shows he runs SEU's network admin team; stack is Cisco plus Meraki." ,
1141+ ) ,
1142+ ) . toBe ( false ) ;
1143+ } ) ;
1144+
1145+ describe ( "lineRangeOverlapsDreamingFence" , ( ) => {
1146+ it ( "returns true when the range falls inside a Light Sleep fence" , ( ) => {
1147+ const lines = [
1148+ "# Daily note" ,
1149+ "## Notes" ,
1150+ "Real durable content." ,
1151+ "## Light Sleep" ,
1152+ "<!-- openclaw:dreaming:light:start -->" ,
1153+ "- Candidate: some staged dream content" ,
1154+ "<!-- openclaw:dreaming:light:end -->" ,
1155+ "## After" ,
1156+ "More real content." ,
1157+ ] ;
1158+ // Line 6 (1-indexed) sits between the fence markers.
1159+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 6 , 6 ) ) . toBe ( true ) ;
1160+ } ) ;
1161+
1162+ it ( "returns false when the range sits entirely outside any dreaming fence" , ( ) => {
1163+ const lines = [
1164+ "# Daily note" ,
1165+ "Real durable content." ,
1166+ "<!-- openclaw:dreaming:rem:start -->" ,
1167+ "staged dream content" ,
1168+ "<!-- openclaw:dreaming:rem:end -->" ,
1169+ "More real content." ,
1170+ ] ;
1171+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 2 , 2 ) ) . toBe ( false ) ;
1172+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 6 , 6 ) ) . toBe ( false ) ;
1173+ } ) ;
1174+
1175+ it ( "returns true when the range straddles a fence boundary" , ( ) => {
1176+ const lines = [
1177+ "real line 1" ,
1178+ "<!-- openclaw:dreaming:diary:start -->" ,
1179+ "dream line" ,
1180+ "<!-- openclaw:dreaming:diary:end -->" ,
1181+ "real line 5" ,
1182+ ] ;
1183+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 2 , 4 ) ) . toBe ( true ) ;
1184+ } ) ;
1185+
1186+ it ( "recovers after a fence end so later real content is not flagged" , ( ) => {
1187+ const lines = [
1188+ "<!-- openclaw:dreaming:light:start -->" ,
1189+ "dream" ,
1190+ "<!-- openclaw:dreaming:light:end -->" ,
1191+ "real line 4" ,
1192+ "<!-- openclaw:dreaming:rem:start -->" ,
1193+ "more dream" ,
1194+ "<!-- openclaw:dreaming:rem:end -->" ,
1195+ "real line 8" ,
1196+ ] ;
1197+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 4 , 4 ) ) . toBe ( false ) ;
1198+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 8 , 8 ) ) . toBe ( false ) ;
1199+ expect ( __testing . lineRangeOverlapsDreamingFence ( lines , 6 , 6 ) ) . toBe ( true ) ;
1200+ } ) ;
1201+ } ) ;
1202+
1203+ it ( "refuses to promote rehydrated candidates that land inside a managed dreaming fence" , async ( ) => {
1204+ await withTempWorkspace ( async ( workspaceDir ) => {
1205+ const dailyPath = await writeDailyMemoryNote ( workspaceDir , "2026-04-18" , [
1206+ "# 2026-04-18" ,
1207+ "" ,
1208+ "## Notes" ,
1209+ "Legitimate durable observation about backups." ,
1210+ "" ,
1211+ "## Light Sleep" ,
1212+ "<!-- openclaw:dreaming:light:start -->" ,
1213+ "- Candidate: staged dream scratchwork" ,
1214+ "<!-- openclaw:dreaming:light:end -->" ,
1215+ ] ) ;
1216+ expect ( dailyPath ) . toBeTruthy ( ) ;
1217+
1218+ const applied = await applyShortTermPromotions ( {
1219+ workspaceDir,
1220+ minScore : 0 ,
1221+ minRecallCount : 0 ,
1222+ minUniqueQueries : 0 ,
1223+ candidates : [
1224+ {
1225+ key : "memory:memory/2026-04-18.md:8:8" ,
1226+ path : "memory/2026-04-18.md" ,
1227+ startLine : 8 ,
1228+ endLine : 8 ,
1229+ source : "memory" ,
1230+ snippet : "- Candidate: staged dream scratchwork" ,
1231+ recallCount : 3 ,
1232+ avgScore : 0.9 ,
1233+ maxScore : 0.9 ,
1234+ uniqueQueries : 2 ,
1235+ firstRecalledAt : "2026-04-17T00:00:00.000Z" ,
1236+ lastRecalledAt : "2026-04-18T00:00:00.000Z" ,
1237+ ageDays : 1 ,
1238+ score : 0.9 ,
1239+ recallDays : [ "2026-04-17" , "2026-04-18" ] ,
1240+ conceptTags : [ "dream" ] ,
1241+ components : {
1242+ frequency : 1 ,
1243+ relevance : 0 ,
1244+ diversity : 1 ,
1245+ recency : 1 ,
1246+ consolidation : 0 ,
1247+ conceptual : 0 ,
1248+ } ,
1249+ } ,
1250+ ] ,
1251+ } ) ;
1252+
1253+ expect ( applied . applied ) . toBe ( 0 ) ;
1254+ const memoryText = await fs
1255+ . readFile ( path . join ( workspaceDir , "MEMORY.md" ) , "utf-8" )
1256+ . catch ( ( ) => "" ) ;
1257+ expect ( memoryText ) . not . toContain ( "Promoted From Short-Term Memory" ) ;
1258+ expect ( memoryText ) . not . toContain ( "staged dream scratchwork" ) ;
1259+ } ) ;
1260+ } ) ;
1261+
11211262 it ( "skips direct candidates that exceed maxAgeDays during apply" , async ( ) => {
11221263 await withTempWorkspace ( async ( workspaceDir ) => {
11231264 const applied = await applyShortTermPromotions ( {
0 commit comments