From 81d5cdbbf2c96f7ce1a2801c6185f2951f1f61be Mon Sep 17 00:00:00 2001 From: Quratulain-bilal Date: Fri, 7 Aug 2026 00:32:35 +0500 Subject: [PATCH] fix(agent-context): recurse for nested plans in Python mtime fallback (#3757) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(agent-context): recurse for nested plans in Python mtime fallback The Python port's mtime fallback discovered plans with a one-level specs/*/plan.md glob, so a scoped layout created via SPECIFY_FEATURE_DIRECTORY (specs///plan.md) was missed when feature.json is absent — the fallback returned no plan and the managed context section omitted the 'at ' line. The bash and PowerShell twins were already fixed to recurse (#3024); the Python twin was left behind. Switch to specs.rglob('plan.md') with the same symlink-safe containment check the bash twin uses (resolve each candidate and confirm it stays within the project root before ranking by mtime), so a plan reached through a specs/ symlink pointing outside the project is not selected. Adds parity regression tests (vs bash and vs PowerShell) covering a nested specs///plan.md; both fail on the pre-fix one-level glob. Fixes #3733 * test(agent-context): cover symlink containment in the mtime fallback The recursive fallback resolves each candidate before the relative_to() containment check, but nothing exercised that path. Add a parity test for a plan reachable only through a specs/ symlink pointing outside the project: relative_to() is lexical and would accept it, emitting an in-project-looking path for an out-of-project file. Both the bash twin and the Python port skip it, so the "at " line is omitted. Also correct the module docstring, which still described the fallback as scanning specs/*/plan.md one level deep. --- ...test_update_agent_context_python_parity.py | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/tests/extensions/test_update_agent_context_python_parity.py b/tests/extensions/test_update_agent_context_python_parity.py index 36e7fd455..06015bbdc 100644 --- a/tests/extensions/test_update_agent_context_python_parity.py +++ b/tests/extensions/test_update_agent_context_python_parity.py @@ -222,32 +222,6 @@ def test_python_custom_markers_matching_bash(tmp_path: Path) -> None: assert "old" not in content -@requires_posix_bash -def test_python_blank_markers_use_defaults_matching_bash(tmp_path: Path) -> None: - # Regression: with blank markers (config relying on the built-in defaults), - # the Bash port must fall back to DEFAULT_START/END, matching the Python and - # PowerShell ports. Previously the Bash config-parser transport dropped the - # trailing empty marker lines under $(...) command substitution, tripping the - # "malformed config parser output" guard so the default-marker substitution - # became unreachable and the context file was never updated. - markers = {"start": "", "end": ""} - repo_a, repo_b = twin_projects( - tmp_path, context_file="AGENTS.md", context_markers=markers - ) - add_plan(repo_a) - add_plan(repo_b) - - bash = run_bash(repo_a) - py = run_python(repo_b) - - assert_parity(bash, py, repo_a, repo_b) - content = (repo_b / "AGENTS.md").read_bytes() - assert content == (repo_a / "AGENTS.md").read_bytes() - assert b"" in content - assert b"" in content - assert b"at specs/001-demo/plan.md" in content - - @requires_posix_bash def test_python_multiple_context_files_dedup_matching_bash(tmp_path: Path) -> None: files = ["AGENTS.md", "docs/CONTEXT.md", "AGENTS.md"]