11"""Tests for the scheduling pre-step in workflows/autoloop.md.
22
3- Functions are extracted directly from the workflow heredoc at import time
4- (see conftest.py) — there is no separate copy of the scheduling code.
3+ Functions are extracted directly from the workflow JavaScript heredoc at import
4+ time (see conftest.py) and called via Node.js subprocess — there is no separate
5+ copy of the scheduling code.
56
67For inline logic (slugify, frontmatter parsing, skip conditions, etc.) that
7- isn't wrapped in a function def in the workflow, we write thin test helpers
8+ isn't wrapped in a named function in the workflow, we write thin test helpers
89that replicate the exact inline pattern. These are documented with the
9- workflow source lines they correspond to.
10+ workflow source patterns they correspond to.
1011"""
1112
1213import re
2728# ---------------------------------------------------------------------------
2829
2930def slugify_issue_title (title ):
30- """Replicates the inline slug logic at workflows/autoloop.md lines 236-237 ."""
31+ """Replicates the inline slug logic in the workflow's issue scanning section ."""
3132 slug = re .sub (r'[^a-z0-9]+' , '-' , title .lower ()).strip ('-' )
3233 slug = re .sub (r'-+' , '-' , slug )
3334 return slug
3435
3536
3637def parse_frontmatter (content ):
37- """Replicates the inline frontmatter parsing at workflows/autoloop.md lines 316-330 ."""
38+ """Replicates the inline frontmatter parsing in the workflow's program scanning loop ."""
3839 content_stripped = re .sub (r'^(\s*<!--.*?-->\s*\n)*' , '' , content , flags = re .DOTALL )
3940 schedule_delta = None
4041 target_metric = None
@@ -53,7 +54,7 @@ def parse_frontmatter(content):
5354
5455
5556def is_unconfigured (content ):
56- """Replicates the inline unconfigured check at workflows/autoloop.md lines 306-312 ."""
57+ """Replicates the inline unconfigured check in the workflow's program scanning loop ."""
5758 if "<!-- AUTOLOOP:UNCONFIGURED -->" in content :
5859 return True
5960 if re .search (r'\bTODO\b|\bREPLACE' , content ):
@@ -62,7 +63,7 @@ def is_unconfigured(content):
6263
6364
6465def check_skip_conditions (state ):
65- """Replicates the inline skip logic at workflows/autoloop.md lines 347-361 .
66+ """Replicates the inline skip logic in the workflow's program scanning loop .
6667
6768 Returns (should_skip, reason).
6869 """
@@ -80,7 +81,7 @@ def check_skip_conditions(state):
8081
8182
8283def check_if_due (schedule_delta , last_run , now ):
83- """Replicates the inline due check at workflows/autoloop.md lines 363-368 .
84+ """Replicates the inline due check in the workflow's program scanning loop .
8485
8586 Returns (is_due, next_due_iso).
8687 """
@@ -91,7 +92,7 @@ def check_if_due(schedule_delta, last_run, now):
9192
9293
9394def select_program (due , forced_program = None , all_programs = None , unconfigured = None , issue_programs = None ):
94- """Replicates the selection logic at workflows/autoloop.md lines 379-409 .
95+ """Replicates the selection logic in the workflow's program selection section .
9596
9697 Returns (selected, selected_file, selected_issue, selected_target_metric, deferred, error).
9798 """
@@ -312,7 +313,7 @@ def test_absolute_path_directory(self):
312313
313314
314315# ---------------------------------------------------------------------------
315- # slugify_issue_title (inline pattern, lines 236-237 )
316+ # slugify_issue_title (inline pattern, issue scanning section )
316317# ---------------------------------------------------------------------------
317318
318319class TestSlugifyIssueTitle :
@@ -345,7 +346,7 @@ def test_consecutive_hyphens_collapsed(self):
345346 assert slugify_issue_title ("a b c" ) == "a-b-c"
346347
347348 def test_collision_dedup (self ):
348- """Replicates the slug collision dedup at workflows/autoloop.md lines 240-242 ."""
349+ """Replicates the slug collision dedup in the workflow's issue scanning section ."""
349350 # Simulate two issues that slugify to the same name
350351 issue_programs = {}
351352 titles = [("Improve Tests" , 10 ), ("improve-tests" , 20 )]
@@ -363,7 +364,7 @@ def test_collision_dedup(self):
363364
364365
365366# ---------------------------------------------------------------------------
366- # parse_frontmatter (inline pattern, lines 316-330 )
367+ # parse_frontmatter (inline pattern, program scanning loop )
367368# ---------------------------------------------------------------------------
368369
369370class TestParseFrontmatter :
@@ -416,7 +417,7 @@ def test_extra_frontmatter_fields_ignored(self):
416417
417418
418419# ---------------------------------------------------------------------------
419- # is_unconfigured (inline pattern, lines 306-312 )
420+ # is_unconfigured (inline pattern, program scanning loop )
420421# ---------------------------------------------------------------------------
421422
422423class TestIsUnconfigured :
@@ -453,7 +454,7 @@ def test_issue_template_detected(self):
453454
454455
455456# ---------------------------------------------------------------------------
456- # check_skip_conditions (inline pattern, lines 347-361 )
457+ # check_skip_conditions (inline pattern, program scanning loop )
457458# ---------------------------------------------------------------------------
458459
459460class TestCheckSkipConditions :
@@ -512,7 +513,7 @@ def test_completed_takes_priority_over_paused(self):
512513
513514
514515# ---------------------------------------------------------------------------
515- # check_if_due (inline pattern, lines 363-368 )
516+ # check_if_due (inline pattern, program scanning loop )
516517# ---------------------------------------------------------------------------
517518
518519class TestCheckIfDue :
@@ -556,7 +557,7 @@ def test_next_due_timestamp(self):
556557
557558
558559# ---------------------------------------------------------------------------
559- # select_program (inline pattern, lines 379-409 )
560+ # select_program (inline pattern, program selection section )
560561# ---------------------------------------------------------------------------
561562
562563class TestSelectProgram :
@@ -646,7 +647,7 @@ def test_forced_program_gets_target_metric_from_due(self):
646647 def test_forced_program_not_in_due_select_returns_none (self ):
647648 # select_program itself returns None for target_metric when program isn't in due.
648649 # The workflow's forced-program path has a fallback that parses target_metric
649- # directly from the program file (workflows/autoloop.md lines 399-410 ).
650+ # directly from the program file (see forced-program fallback in the workflow ).
650651 due = []
651652 all_progs = {"a" : "a.md" }
652653 selected , file , issue , target , deferred , err = select_program (
0 commit comments