@@ -222,32 +222,6 @@ def test_python_custom_markers_matching_bash(tmp_path: Path) -> None:
222222 assert "old" not in content
223223
224224
225- @requires_posix_bash
226- def test_python_blank_markers_use_defaults_matching_bash (tmp_path : Path ) -> None :
227- # Regression: with blank markers (config relying on the built-in defaults),
228- # the Bash port must fall back to DEFAULT_START/END, matching the Python and
229- # PowerShell ports. Previously the Bash config-parser transport dropped the
230- # trailing empty marker lines under $(...) command substitution, tripping the
231- # "malformed config parser output" guard so the default-marker substitution
232- # became unreachable and the context file was never updated.
233- markers = {"start" : "" , "end" : "" }
234- repo_a , repo_b = twin_projects (
235- tmp_path , context_file = "AGENTS.md" , context_markers = markers
236- )
237- add_plan (repo_a )
238- add_plan (repo_b )
239-
240- bash = run_bash (repo_a )
241- py = run_python (repo_b )
242-
243- assert_parity (bash , py , repo_a , repo_b )
244- content = (repo_b / "AGENTS.md" ).read_bytes ()
245- assert content == (repo_a / "AGENTS.md" ).read_bytes ()
246- assert b"<!-- SPECKIT START -->" in content
247- assert b"<!-- SPECKIT END -->" in content
248- assert b"at specs/001-demo/plan.md" in content
249-
250-
251225@requires_posix_bash
252226def test_python_multiple_context_files_dedup_matching_bash (tmp_path : Path ) -> None :
253227 files = ["AGENTS.md" , "docs/CONTEXT.md" , "AGENTS.md" ]
@@ -344,14 +318,19 @@ def test_python_mtime_fallback_matching_bash(tmp_path: Path) -> None:
344318
345319
346320@requires_posix_bash
347- def test_python_mtime_fallback_finds_nested_plan_matching_bash (tmp_path : Path ) -> None :
348- # Regression: the mtime fallback must discover plan.md in nested scoped
349- # layouts (specs/<scope>/<feature>/plan.md), matching the Bash/PowerShell
350- # ports and the documented recursive-discovery contract (see #3024). A
351- # one-level scan (specs/*/plan.md) would miss this and omit the plan link.
321+ def test_python_mtime_fallback_finds_nested_plan_matching_bash (
322+ tmp_path : Path ,
323+ ) -> None :
324+ """The mtime fallback must recurse into scoped layouts.
325+
326+ A plan created under specs/<scope>/<feature>/plan.md (as produced via
327+ SPECIFY_FEATURE_DIRECTORY) is more than one level below specs/. The old
328+ Python port used a one-level specs/*/plan.md glob and missed it, while the
329+ bash/PowerShell twins recurse (#3024). This locks in the parity.
330+ """
352331 repo_a , repo_b = twin_projects (tmp_path , context_file = "AGENTS.md" )
353332 for repo in (repo_a , repo_b ):
354- plan = repo / "specs" / "scope-a " / "002 -nested" / "plan.md"
333+ plan = repo / "specs" / "backend " / "001 -nested" / "plan.md"
355334 plan .parent .mkdir (parents = True , exist_ok = True )
356335 plan .write_text ("# plan\n " , encoding = "utf-8" )
357336
@@ -361,7 +340,7 @@ def test_python_mtime_fallback_finds_nested_plan_matching_bash(tmp_path: Path) -
361340 assert_parity (bash , py , repo_a , repo_b )
362341 content = (repo_b / "AGENTS.md" ).read_bytes ()
363342 assert content == (repo_a / "AGENTS.md" ).read_bytes ()
364- assert b"at specs/scope-a/002 -nested/plan.md" in content
343+ assert b"at specs/backend/001 -nested/plan.md" in content
365344
366345
367346@requires_posix_bash
@@ -508,6 +487,31 @@ def test_python_fresh_context_file_matches_powershell(tmp_path: Path) -> None:
508487 assert (repo_a / "AGENTS.md" ).read_bytes () == (repo_b / "AGENTS.md" ).read_bytes ()
509488
510489
490+ @pytest .mark .skipif (not POWERSHELL , reason = "no PowerShell available" )
491+ def test_python_mtime_fallback_finds_nested_plan_matches_powershell (
492+ tmp_path : Path ,
493+ ) -> None :
494+ """Python's mtime fallback must recurse like the PowerShell twin.
495+
496+ With no feature.json, discovery falls back to scanning under specs/. A plan
497+ at specs/<scope>/<feature>/plan.md sits more than one level deep; the old
498+ Python one-level glob missed it while PowerShell already recurses (#3024).
499+ """
500+ repo_a = make_project (tmp_path / "proj-ps" , context_file = "AGENTS.md" )
501+ repo_b = make_project (tmp_path / "proj-py" , context_file = "AGENTS.md" )
502+ for repo in (repo_a , repo_b ):
503+ plan = repo / "specs" / "backend" / "001-nested" / "plan.md"
504+ plan .parent .mkdir (parents = True , exist_ok = True )
505+ plan .write_text ("# plan\n " , encoding = "utf-8" )
506+
507+ ps = run_powershell (repo_a )
508+ py = run_python (repo_b )
509+
510+ assert ps .returncode == py .returncode == 0 , ps .stderr + py .stderr
511+ assert (repo_a / "AGENTS.md" ).read_bytes () == (repo_b / "AGENTS.md" ).read_bytes ()
512+ assert b"at specs/backend/001-nested/plan.md" in (repo_b / "AGENTS.md" ).read_bytes ()
513+
514+
511515@pytest .mark .skipif (not POWERSHELL , reason = "no PowerShell available" )
512516def test_python_upsert_matches_powershell (tmp_path : Path ) -> None :
513517 repo_a = make_project (tmp_path / "proj-ps" , context_file = "AGENTS.md" )
0 commit comments