You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
chore: gate03-sweep.sh --compare keys on absolute paths and silently treats a directory with no *.json as an empty baseline #182
Two hazards in one comparison mechanism, each of which has already cost this project a spurious result.
A directory with no *.json loads as a silent empty baseline.--compare globs *.json in each argument. This repository deliberately commits no raw reports (only manifest.tsv, summary.tsv, checksums.sha256), so pointing --compare at a committed sweep directory loads zero findings and reports every real finding in the candidate as a new addition (a spurious 500-line diff in Phase 4 plan 04-07). It should refuse to compare an empty side with a clear error.
Findings are keyed on the absolute path recorded in the JSON. A sweep captured while the repository was a disposable agent worktree embeds that worktree's path for the repo-local input rows, so a later run from another checkout sees those rows as wholesale additions and removals. Phase 5 plan 05-01 hit this, and the orchestrator had to re-capture three rows from the main checkout. The key should be relative to a declared root (or the script should refuse repo-local rows that are not under the current root).
Both are mitigated today by procedure (always point --compare at .planning/local/; sweep repo-local inputs by the main checkout's literal paths), which is exactly the kind of mitigation that is forgotten.
Filed at the close-out of Phase 5 (CAT-03, #35), plan 05-07 Task 2, from .planning/phases/05-persistence-lifecycle-hijack-cat-03-35/deferred-items.md. Nothing here is fixed in v0.2.0.
Both hazards still present on main @ edec71c. Flagging explicitly because PR #192 recently touched this same script (fix: keep sweep helper variables local) and that was a different bug — variable isolation, not comparison semantics. This issue was not affected by it.
Hazard 1 reproduced empirically. A baseline directory containing no *.json — which is exactly what a committed sweep directory looks like here, since the repo commits only manifest.tsv, summary.tsv and checksums.sha256:
$ ls base/ -> manifest.tsv
$ ls cand/ -> r.json (2 findings)
$ ./scripts/gate03-sweep.sh --compare base cand
/abs/path/doc.md:7 PI001
/abs/path/doc.md:9 PI031
exit 1
Every candidate finding is reported as a new addition, exit 1, and no error or warning is raised. load_findings() returns an empty set() for a directory with no matching glob, and nothing downstream distinguishes "no findings" from "no input".
Hazard 2 confirmed by inspection.findings.add((report['file'], match['line'], match['pattern_id'])) keys on report['file'], which is the absolute path as scanned. Two sweeps of the same content from different checkout paths therefore share no keys, and the whole candidate reads as new.
Smallest fix that closes hazard 1 without touching the comparison logic: have load_findings count the files it globbed and fail with a clear message when the glob matched nothing, so an empty side is refused rather than silently treated as a clean baseline.
Not scheduling — maintenance-only after v0.2.0 — but worth noting that of the two, hazard 1 is the one that has already produced a spurious result twice (the 500-line Phase 4 plan 04-07 diff cited above, and the GATE-03 --compare exit code investigated in quick-260902-jhy).
Activity
hermanngeorge15 commentedon Oct 22, 2026
Both hazards still present on
main@edec71c. Flagging explicitly because PR #192 recently touched this same script (fix: keep sweep helper variables local) and that was a different bug — variable isolation, not comparison semantics. This issue was not affected by it.Hazard 1 reproduced empirically. A baseline directory containing no
*.json— which is exactly what a committed sweep directory looks like here, since the repo commits onlymanifest.tsv,summary.tsvandchecksums.sha256:Every candidate finding is reported as a new addition, exit 1, and no error or warning is raised.
load_findings()returns an emptyset()for a directory with no matching glob, and nothing downstream distinguishes "no findings" from "no input".Hazard 2 confirmed by inspection.
findings.add((report['file'], match['line'], match['pattern_id']))keys onreport['file'], which is the absolute path as scanned. Two sweeps of the same content from different checkout paths therefore share no keys, and the whole candidate reads as new.Smallest fix that closes hazard 1 without touching the comparison logic: have
load_findingscount the files it globbed and fail with a clear message when the glob matched nothing, so an empty side is refused rather than silently treated as a clean baseline.Not scheduling — maintenance-only after v0.2.0 — but worth noting that of the two, hazard 1 is the one that has already produced a spurious result twice (the 500-line Phase 4 plan 04-07 diff cited above, and the GATE-03
--compareexit code investigated inquick-260902-jhy).