Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

Cross-platform path fix (FN-029), caught by the Windows CI matrix that Linux-only local runs had been missing.

- `--files` glob expansion wrote OS-native separators into `docs/TASKS.md` — backslashes on Windows. Because the committed scope block is matched against `git` output (always forward-slash) by the drive-check scope gate, Windows-declared scopes silently failed to match their own changed files, and the committed artifact was not portable across machines.
- The pattern is now normalized to forward-slash BEFORE globbing (so a Windows-style `src\dir\*.ts` still expands) and every stored path uses `Path.as_posix()`. The done-report backlinks written into `TASKS.md`/`PROGRESS.md` are normalized the same way.
- The FN-021 regression test now feeds a backslash-style pattern and asserts the committed Allowed-scope block contains no backslash, so the contract is enforced even on Linux-only runs (76 total).

## v0.8.4

Close-before-report ordering fix: closes the fourth round of field findings (FN-027/FN-028) and corrects the root-cause analysis of FN-023. The theme: the closeout ledger runs from a snapshot taken before anything mutates, and every verdict printed matches what actually happened on disk.
Expand Down
11 changes: 8 additions & 3 deletions scripts/coderail.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def write_done_report(root: Path, shown: str, title: str,
lines += [f"- {w}" for w in tdd_warnings] + [""]
lines += ["## Full gate output", "", "```", gate_output.rstrip(), "```", ""]
path.write_text("\n".join(lines), encoding="utf-8")
return str(path.relative_to(root))
return path.relative_to(root).as_posix() # FN-029: portable path in TASKS/PROGRESS


# ------------------------------------------------- blueprint coverage
Expand Down Expand Up @@ -722,9 +722,14 @@ def cmd_start(args) -> int:
raw_files += [f.strip() for f in chunk.split(",") if f.strip()]
files: list[str] = []
for pat in raw_files:
# FN-029: TASKS.md is a committed, cross-platform artifact whose paths
# are matched against git output (always forward-slash). Normalize the
# pattern to forward-slash BEFORE globbing (so a Windows-style
# "src\x\*.ts" still expands), and store results forward-slash too.
pat = pat.replace("\\", "/")
if any(ch in pat for ch in "*?["):
matches = sorted(
str(p.relative_to(root)) for p in root.glob(pat) if p.is_file()
p.relative_to(root).as_posix() for p in root.glob(pat) if p.is_file()
)
files += matches or [pat]
else:
Expand Down Expand Up @@ -1268,7 +1273,7 @@ def cmd_progress(args) -> int:
else:
checked = "retroactive entry - no verify commands were registered"
if matching:
checked += f"; surviving report: {matching[-1].relative_to(root)}"
checked += f"; surviving report: {matching[-1].relative_to(root).as_posix()}"

next_hint = snap.get("next_hint") or "decide with the user"
accepted = list(zip(snap.get("accept_items", []),
Expand Down
10 changes: 9 additions & 1 deletion tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1224,14 +1224,22 @@ def test_files_globs_expand_and_accumulate():
for n in ['director_core.ts', 'director_utils.ts']:
(root/'src/director'/n).write_text('export {}\n', encoding='utf-8')
r = cr('start', 'Glob scope task',
'--files', 'src/director/director*.ts',
# FN-029: a backslash-style pattern must still expand and, more
# importantly, must be stored forward-slash - TASKS.md is a
# committed artifact matched against git output on every OS.
'--files', r'src\director\director*.ts',
'--files', 'docs/NOTES.md,README.md',
'--verify', 'true')
check(r.returncode == 0, r.stdout)
tasks = (root/'docs/TASKS.md').read_text(encoding='utf-8')
for expect in ['src/director/director_core.ts', 'src/director/director_utils.ts',
'docs/NOTES.md', 'README.md']:
check(f'- {expect}' in tasks, f'missing expanded file {expect}: {tasks[-800:]}')
# Narrow to THIS task's Allowed scope block (the template's example
# task legitimately contains an escaped "\##" heading elsewhere).
allowed = tasks[tasks.index('Allowed:'):tasks.index('Forbidden:')]
check('\\' not in allowed,
f'FN-029: backslash leaked into committed TASKS scope: {allowed!r}')


def test_shim_probes_candidate_homes():
Expand Down
Loading