@@ -228,56 +228,162 @@ Read WORKFLOW.md on startup.
228228 expect ( result ) . toContain ( "Current time:" ) ;
229229 } ) ;
230230
231- it ( "falls back to legacy section names (Every Session / Safety)" , async ( ) => {
232- const content = `# Rules
233-
234- ## Every Session
235-
236- Read SOUL.md and USER.md.
237-
238- ## Safety
239-
240- Don't exfiltrate private data.
241-
242- ## Other
243-
244- Ignore this.
245- ` ;
246- fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
247- const result = await readPostCompactionContext ( tmpDir ) ;
248- expect ( result ) . not . toBeNull ( ) ;
249- expect ( result ) . toContain ( "Every Session" ) ;
250- expect ( result ) . toContain ( "Read SOUL.md" ) ;
251- expect ( result ) . toContain ( "Safety" ) ;
252- expect ( result ) . toContain ( "Don't exfiltrate" ) ;
253- expect ( result ) . not . toContain ( "Other" ) ;
254- } ) ;
255-
256- it ( "prefers new section names over legacy when both exist" , async ( ) => {
257- const content = `# Rules
258-
259- ## Session Startup
260-
261- New startup instructions.
262-
263- ## Every Session
264-
265- Old startup instructions.
266-
267- ## Red Lines
268-
269- New red lines.
270-
271- ## Safety
272-
273- Old safety rules.
274- ` ;
275- fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
276- const result = await readPostCompactionContext ( tmpDir ) ;
277- expect ( result ) . not . toBeNull ( ) ;
278- expect ( result ) . toContain ( "New startup instructions" ) ;
279- expect ( result ) . toContain ( "New red lines" ) ;
280- expect ( result ) . not . toContain ( "Old startup instructions" ) ;
281- expect ( result ) . not . toContain ( "Old safety rules" ) ;
231+ // -------------------------------------------------------------------------
232+ // postCompactionSections config
233+ // -------------------------------------------------------------------------
234+ describe ( "agents.defaults.compaction.postCompactionSections" , ( ) => {
235+ it ( "uses default sections (Session Startup + Red Lines) when config is not set" , async ( ) => {
236+ const content = `## Session Startup\n\nDo startup.\n\n## Red Lines\n\nDo not break.\n\n## Other\n\nIgnore.\n` ;
237+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
238+ const result = await readPostCompactionContext ( tmpDir ) ;
239+ expect ( result ) . toContain ( "Session Startup" ) ;
240+ expect ( result ) . toContain ( "Red Lines" ) ;
241+ expect ( result ) . not . toContain ( "Other" ) ;
242+ } ) ;
243+
244+ it ( "uses custom section names from config instead of defaults" , async ( ) => {
245+ const content = `## Session Startup\n\nDo startup.\n\n## Critical Rules\n\nMy custom rules.\n\n## Red Lines\n\nDefault section.\n` ;
246+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
247+ const cfg = {
248+ agents : {
249+ defaults : {
250+ compaction : { postCompactionSections : [ "Critical Rules" ] } ,
251+ } ,
252+ } ,
253+ } as OpenClawConfig ;
254+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
255+ expect ( result ) . not . toBeNull ( ) ;
256+ expect ( result ) . toContain ( "Critical Rules" ) ;
257+ expect ( result ) . toContain ( "My custom rules" ) ;
258+ // Default sections must not be included when overridden
259+ expect ( result ) . not . toContain ( "Do startup" ) ;
260+ expect ( result ) . not . toContain ( "Default section" ) ;
261+ } ) ;
262+
263+ it ( "supports multiple custom section names" , async ( ) => {
264+ const content = `## Onboarding\n\nOnboard things.\n\n## Safety\n\nSafe things.\n\n## Noise\n\nIgnore.\n` ;
265+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
266+ const cfg = {
267+ agents : {
268+ defaults : {
269+ compaction : { postCompactionSections : [ "Onboarding" , "Safety" ] } ,
270+ } ,
271+ } ,
272+ } as OpenClawConfig ;
273+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
274+ expect ( result ) . not . toBeNull ( ) ;
275+ expect ( result ) . toContain ( "Onboard things" ) ;
276+ expect ( result ) . toContain ( "Safe things" ) ;
277+ expect ( result ) . not . toContain ( "Ignore" ) ;
278+ } ) ;
279+
280+ it ( "returns null when postCompactionSections is explicitly set to [] (opt-out)" , async ( ) => {
281+ const content = `## Session Startup\n\nDo startup.\n\n## Red Lines\n\nDo not break.\n` ;
282+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
283+ const cfg = {
284+ agents : {
285+ defaults : {
286+ compaction : { postCompactionSections : [ ] } ,
287+ } ,
288+ } ,
289+ } as OpenClawConfig ;
290+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
291+ // Empty array = opt-out: no post-compaction context injection
292+ expect ( result ) . toBeNull ( ) ;
293+ } ) ;
294+
295+ it ( "returns null when custom sections are configured but none found in AGENTS.md" , async ( ) => {
296+ const content = `## Session Startup\n\nDo startup.\n` ;
297+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
298+ const cfg = {
299+ agents : {
300+ defaults : {
301+ compaction : { postCompactionSections : [ "Nonexistent Section" ] } ,
302+ } ,
303+ } ,
304+ } as OpenClawConfig ;
305+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
306+ expect ( result ) . toBeNull ( ) ;
307+ } ) ;
308+
309+ it ( "does NOT reference 'Session Startup' in prose when custom sections are configured" , async ( ) => {
310+ // Greptile review finding: hardcoded prose mentioned "Execute your Session Startup
311+ // sequence now" even when custom section names were configured, causing agents to
312+ // look for a non-existent section. Prose must adapt to the configured section names.
313+ const content = `## Boot Sequence\n\nDo custom boot things.\n` ;
314+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
315+ const cfg = {
316+ agents : {
317+ defaults : {
318+ compaction : { postCompactionSections : [ "Boot Sequence" ] } ,
319+ } ,
320+ } ,
321+ } as OpenClawConfig ;
322+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
323+ expect ( result ) . not . toBeNull ( ) ;
324+ // Must not reference the hardcoded default section name
325+ expect ( result ) . not . toContain ( "Session Startup" ) ;
326+ // Must reference the actual configured section names
327+ expect ( result ) . toContain ( "Boot Sequence" ) ;
328+ } ) ;
329+
330+ it ( "uses default 'Session Startup' prose when default sections are active" , async ( ) => {
331+ const content = `## Session Startup\n\nDo startup.\n` ;
332+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
333+ const result = await readPostCompactionContext ( tmpDir ) ;
334+ expect ( result ) . not . toBeNull ( ) ;
335+ expect ( result ) . toContain ( "Execute your Session Startup sequence now" ) ;
336+ } ) ;
337+
338+ it ( "falls back to legacy sections when defaults are explicitly configured" , async ( ) => {
339+ // Older AGENTS.md templates use "Every Session" / "Safety" instead of
340+ // "Session Startup" / "Red Lines". Explicitly setting the defaults should
341+ // still trigger the legacy fallback — same behavior as leaving the field unset.
342+ const content = `## Every Session\n\nDo startup things.\n\n## Safety\n\nBe safe.\n` ;
343+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
344+ const cfg = {
345+ agents : {
346+ defaults : {
347+ compaction : { postCompactionSections : [ "Session Startup" , "Red Lines" ] } ,
348+ } ,
349+ } ,
350+ } as OpenClawConfig ;
351+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
352+ expect ( result ) . not . toBeNull ( ) ;
353+ expect ( result ) . toContain ( "Do startup things" ) ;
354+ expect ( result ) . toContain ( "Be safe" ) ;
355+ } ) ;
356+
357+ it ( "falls back to legacy sections when default sections are configured in a different order" , async ( ) => {
358+ const content = `## Every Session\n\nDo startup things.\n\n## Safety\n\nBe safe.\n` ;
359+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
360+ const cfg = {
361+ agents : {
362+ defaults : {
363+ compaction : { postCompactionSections : [ "Red Lines" , "Session Startup" ] } ,
364+ } ,
365+ } ,
366+ } as OpenClawConfig ;
367+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
368+ expect ( result ) . not . toBeNull ( ) ;
369+ expect ( result ) . toContain ( "Do startup things" ) ;
370+ expect ( result ) . toContain ( "Be safe" ) ;
371+ expect ( result ) . toContain ( "Execute your Session Startup sequence now" ) ;
372+ } ) ;
373+
374+ it ( "custom section names are matched case-insensitively" , async ( ) => {
375+ const content = `## WORKFLOW INIT\n\nInit things.\n` ;
376+ fs . writeFileSync ( path . join ( tmpDir , "AGENTS.md" ) , content ) ;
377+ const cfg = {
378+ agents : {
379+ defaults : {
380+ compaction : { postCompactionSections : [ "workflow init" ] } ,
381+ } ,
382+ } ,
383+ } as OpenClawConfig ;
384+ const result = await readPostCompactionContext ( tmpDir , cfg ) ;
385+ expect ( result ) . not . toBeNull ( ) ;
386+ expect ( result ) . toContain ( "Init things" ) ;
387+ } ) ;
282388 } ) ;
283389} ) ;
0 commit comments