fix: derive plan path from feature.json in update-agent-context#3069
fix: derive plan path from feature.json in update-agent-context#3069amirreza225 wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the agent-context extension’s context refresh scripts so that, when no explicit plan_path argument is provided, they derive the active plan.md from .specify/feature.json (written by /speckit-specify) and only fall back to the “most recently modified specs/*/plan.md” heuristic when that source is unavailable or not yet present. It also adds tests to validate the new feature.json-first behavior.
Changes:
- Bash: Prefer
.specify/feature.jsonto resolve the activeplan.md, with mtime fallback retained. - PowerShell: Implement the same “feature.json-first, mtime-second” selection logic and normalize feature paths.
- Tests: Add coverage ensuring feature.json overrides the mtime heuristic (bash + PowerShell where available).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
extensions/agent-context/scripts/bash/update-agent-context.sh |
Uses .specify/feature.json to select the active plan when plan_path isn’t provided; retains mtime fallback. |
extensions/agent-context/scripts/powershell/update-agent-context.ps1 |
Mirrors bash behavior in PowerShell, including absolute-path normalization and mtime fallback. |
tests/extensions/test_update_agent_context_feature_json.py |
Adds tests validating feature.json-first selection and stale-plan avoidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
mnriem
left a comment
There was a problem hiding this comment.
Please address Copilot feedback
|
All three Copilot comments have been addressed in commit 80600a2:
All 70 tests pass locally. |
Done |
mnriem
left a comment
There was a problem hiding this comment.
Please address Copilot feedback
|
Please address Copilot feedback |
mnriem
left a comment
There was a problem hiding this comment.
Please resolve conflicts by rebasing on upstream/main and fixing any errors
When `plan_path` is omitted, prefer `.specify/feature.json` (written by /speckit-specify) over the mtime heuristic. The old approach picked the most recently modified `specs/*/plan.md`, which could inject an unrelated plan into CLAUDE.md if another spec's plan was touched after the active feature directory was created but before its own plan.md existed. Bash: handle both relative and absolute feature_directory values, normalizing absolute paths back to project-relative for the context file. Fall back to mtime only when feature.json is absent or the derived plan.md does not yet exist. PowerShell: same logic, PS 5.1-compatible (nested Join-Path, IsPathRooted guard to avoid Unix Join-Path mis-joining absolute ChildPaths, manual prefix-strip instead of GetRelativePath). Fixes github#3067
- bash: add explicit encoding="utf-8" to feature.json open() call - powershell: replace GetRelativePath (.NET 5+ only) with manual prefix-strip in mtime fallback for PS 5.1 compatibility - tests: add coverage for absolute feature_directory values (under and outside PROJECT_ROOT)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Valid. The PS tests run on Windows where str(tmp_path) uses backslashes, but the PS script normalizes output to forward slashes. Assertions like assert str(tmp_path) not in ctx become false negatives on Windows CI. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
471c197 to
11530c2
Compare
|
/recheck |
|
Rebased on upstream/main — all conflicts resolved. Could you re-run the checks when you get a chance? Thanks! |
|
Please address Copilot feedback. If you are an agent YOU MUST disclose your work! |
I'm not an agent, but I get help. 😁 |
| @pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") | ||
| def test_ps_ignores_newer_stale_plan_when_feature_json_present(tmp_path: Path) -> None: | ||
| """PowerShell: stale plan touched more recently must not win over feature.json.""" | ||
| _setup_project(tmp_path) | ||
| active = _make_plan(tmp_path, "specs/001-active") | ||
| stale = _make_plan(tmp_path, "specs/000-stale") | ||
| now = time.time() | ||
| os.utime(active, (now - 10, now - 10)) | ||
| os.utime(stale, (now, now)) | ||
| _write_feature_json(tmp_path, "specs/001-active") | ||
|
|
||
| exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL | ||
| result = subprocess.run( | ||
| [exe, "-NoProfile", "-File", str(UPDATE_AGENT_CTX_PS)], | ||
| cwd=tmp_path, | ||
| capture_output=True, | ||
| text=True, | ||
| check=False, | ||
| ) | ||
| assert result.returncode == 0, result.stderr + result.stdout | ||
| ctx = (tmp_path / "CLAUDE.md").read_text(encoding="utf-8") | ||
| assert "specs/001-active/plan.md" in ctx | ||
| assert "specs/000-stale/plan.md" not in ctx |
Summary
update-agent-context.shandupdate-agent-context.ps1now prefer.specify/feature.json(written by/speckit-specify) over the mtime heuristic when no explicitplan_pathargument is givenspecs/*/plan.mdonly whenfeature.jsonis absent or the derivedplan.mddoes not exist yet (e.g./speckit-planhasn't run yet)feature_directoryvalues in both scripts, normalizing to project-relative paths for the context file outputJoin-Path,IsPathRootedguard (UnixJoin-Pathdoes not treat absolute ChildPaths as "wins"), and manual prefix-strip instead ofGetRelativePathFixes #3067
AI disclosure
This PR was written with AI assistance (Claude Code). All changes were reviewed, tested, and understood by me before submission.
Manual test results
Agent: Claude Code | OS/Shell: macOS/zsh
/speckit.specify→/speckit.planCLAUDE.mdreferences the correct active feature'splan.mdCLAUDE.mdstill points to active feature, not the stale oneTest plan
uv run pytest tests/extensions/test_update_agent_context_feature_json.py— 4 new bash tests pass (2 PS skipped, nopwshon CI)uv run pytest tests/test_agent_config_consistency.py tests/extensions/test_extension_agent_context.py— all 62 existing tests pass