mirror of
https://github.com/github/spec-kit.git
synced 2026-08-03 06:26:30 +08:00
fix(agent-context): discover nested plans in Python port mtime fallback (#3734)
The Python port of update-agent-context reintroduced a one-level plan scan (specs/*/plan.md) in its mtime fallback, while the Bash and PowerShell ports search recursively (specs/**/plan.md) per the fix for issue #3024. The three ports were therefore not in parity: for nested scoped layouts such as specs/<scope>/<feature>/plan.md, the Python port found no plan and omitted the plan link from the managed context section. Switch the fallback to `(root / "specs").rglob("plan.md")` and update the module docstring to match the documented recursive-discovery contract. Add a parity regression test covering the nested layout (it fails on the one-level glob and passes with the recursive scan). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,9 @@ Usage: update_agent_context.py [plan_path]
|
||||
|
||||
When ``plan_path`` is omitted, the script derives it from
|
||||
``.specify/feature.json`` (written by /speckit-specify). Falls back to the most
|
||||
recently modified ``specs/*/plan.md`` only when feature.json is absent or its
|
||||
plan does not exist yet.
|
||||
recently modified ``plan.md`` anywhere under ``specs/`` (including nested scoped
|
||||
layouts such as ``specs/<scope>/<feature>/plan.md``) only when feature.json is
|
||||
absent or its plan does not exist yet.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -173,7 +174,7 @@ def _resolve_plan_path(project_root: str) -> str:
|
||||
if not plan_path:
|
||||
root = Path(project_root).resolve()
|
||||
plans = sorted(
|
||||
(root / "specs").glob("*/plan.md"),
|
||||
(root / "specs").rglob("plan.md"),
|
||||
key=lambda p: p.stat().st_mtime,
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
@@ -317,6 +317,27 @@ def test_python_mtime_fallback_matching_bash(tmp_path: Path) -> None:
|
||||
assert b"at specs/001-new/plan.md" in content
|
||||
|
||||
|
||||
@requires_posix_bash
|
||||
def test_python_mtime_fallback_finds_nested_plan_matching_bash(tmp_path: Path) -> None:
|
||||
# Regression: the mtime fallback must discover plan.md in nested scoped
|
||||
# layouts (specs/<scope>/<feature>/plan.md), matching the Bash/PowerShell
|
||||
# ports and the documented recursive-discovery contract (see #3024). A
|
||||
# one-level scan (specs/*/plan.md) would miss this and omit the plan link.
|
||||
repo_a, repo_b = twin_projects(tmp_path, context_file="AGENTS.md")
|
||||
for repo in (repo_a, repo_b):
|
||||
plan = repo / "specs" / "scope-a" / "002-nested" / "plan.md"
|
||||
plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
plan.write_text("# plan\n", encoding="utf-8")
|
||||
|
||||
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"at specs/scope-a/002-nested/plan.md" in content
|
||||
|
||||
|
||||
@requires_posix_bash
|
||||
def test_python_prefers_feature_json_over_mtime_matching_bash(tmp_path: Path) -> None:
|
||||
repo_a, repo_b = twin_projects(tmp_path, context_file="AGENTS.md")
|
||||
|
||||
Reference in New Issue
Block a user