Skip to content

fix(todo-markers): guard three false-verdict paths in stale-marker check - #286

Merged
mtskf merged 10 commits into
mainfrom
chore/check-stale-todo-markers-false-verdicts
Jul 29, 2026
Merged

fix(todo-markers): guard three false-verdict paths in stale-marker check#286
mtskf merged 10 commits into
mainfrom
chore/check-stale-todo-markers-false-verdicts

Conversation

@mtskf

@mtskf mtskf commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes three false-verdict paths in scripts/check-stale-todo-markers.mjs (the local backstop that flags stale in-flight 🚧 TODO markers whose PR merged outside /merge-pr). The script is refactored into pure / dependency-injected exports behind an import-guarded main(), so its logic is unit-testable with in-memory fixtures and a fake ghRunner — no real gh, network, or auth needed.

The three fixes

  • (a) Reused head-branch name — a branch name can be reused after its first PR merged. resolveEntry now prefers an OPEN PR for the head branch, so still-in-flight work is never flagged stale by an older same-named merged PR.
  • (b) Number-only match — PR numbers are reused across repo generations, so a match resting solely on a (#N) / (PR #N) number is now report-only (a warning), never a hard STALE verdict. A conforming in-flight entry names a (branch: X) and resolves via the branch path, which still exits 1 when stale.
  • (c) Transient gh error — a one-off 404 / network blip now skips only that entry (recorded for a partial-scan warning) and keeps scanning, instead of bailing the whole loop.

Error taxonomy & honest exit codes

  • GhUnavailable (ENOENT / auth / rate-limit — all persist for the run) stops the scan but still returns everything already found, so a stale marker confirmed before a mid-scan auth expiry is not silently discarded.
  • GhTransient (single 404, network blip, unparseable JSON) skips one entry and continues.
  • Any other error is a real bug and propagates (never swallowed), surfacing as exit 2 — kept distinct from stale's exit 1 so a crash can't read as "stale found".
  • Exit codes: 0 = clean or partial scan (fail-soft), 1 = a genuine stale marker, 2 = TODO unreadable or an internal error.

Tests

test/build/stale-todo-markers.test.ts — 23 in-memory cases pinning all three fixes plus the error taxonomy, the abort-preserves-stale behaviour, and the honest exit codes (mirrors the sibling test/build/todo-hygiene.test.ts pattern).

Verification

  • pnpm compile ✓ · Biome clean ✓
  • Full unit suite green (4094 tests) · new file 23/23 in isolation
  • Real end-to-end run against the actual TODO: 3 in-flight entries checked via real gh, none stale, exit 0

mtskf added 10 commits July 29, 2026 12:40
Refactor scripts/check-stale-todo-markers.mjs into pure / dependency-injected
exports (behind an import-guarded main()) and fix three false-verdict paths:

- (a) reused head-branch: prefer an OPEN PR over an older same-named MERGED one,
  so still-in-flight work is never flagged stale.
- (b) number-only (bare (#N) / (PR #N)) matches downgrade to a report-only
  warning, never a hard STALE verdict (PR numbers are reused across repo
  generations).
- (c) a transient gh error skips only that entry and keeps scanning; gh being
  unavailable (ENOENT / auth / rate-limit) stops the loop but still returns
  already-confirmed stale, and a taxonomy-external bug surfaces as exit 2
  (never colliding with stale's exit 1).

Add test/build/stale-todo-markers.test.ts (23 in-memory cases with a fake
ghRunner) pinning all three plus the error taxonomy and honest exit codes.
The branch-path staleness check listed --state all --limit 5 and scanned
the slice for an OPEN PR. A busy reused branch could push the in-flight
OPEN PR past the result cap, letting an older same-named merged PR flag
still-open work as stale. Query OPEN independently (--state open) before
looking for a merged PR, so the in-flight guarantee no longer depends on
gh's implicit sort order or a result limit.
An expired or invalid gh token surfaces as "HTTP 401: Bad credentials",
which persists for the whole run, so it belongs with the other run-fatal
auth failures rather than the per-entry transient bucket. Misclassifying
it as transient would silently skip every remaining entry and still exit
0. 403 stays out of the pattern: it can be a genuine per-entry blip.
parseInflight had no direct test — its exclusion of checked (- [x]) and
non-🚧 lines rode only on the higher-level scan tests. Pin the contract
directly so a widened checkbox pattern (e.g. [ ] -> [.]) that lets done
work read as in-flight fails here, and assert scanTodo.inflightCount.
… rot

List parseInflight among the ghRunner-tested exports and split out
classifyGhError (fake error objects) and ghExec (fake exec) as their own
seams so the roster matches what the tests actually exercise. Replace the
orphaned (Finding 2) review-cycle reference on the rethrow comment with
its plain meaning (never swallowed).
The Finding 1/2/4 prefixes named a past review cycle, not the behaviour
under test. Describe the invariant instead; assertions are unchanged.
Consolidate the trailing `export { DEFAULT_TODO }` into an inline
`export const` so every export in the file is declared inline. No
behaviour change.
…alternatives

- Replace the vacuous "no truncation dependency" test with one that
  pins the actual contract: an OPEN PR short-circuits resolveEntry
  before the merged query ever runs (a throwing merged route proves
  it). The old test passed the same either way because a hit on open
  never touched merged.
- Add two classifyGhError cases so the HTTP-401 detection is pinned on
  each regex alternative independently (a bare HTTP 401 with no 'bad
  credentials' text, and a 'Bad credentials' message with no HTTP
  status), instead of relying on a single fixture that satisfies both
  alternatives at once.
… wording

- Correct the header's test-coverage roster: parseInflight, parseEntry,
  and summarize are pure transforms pinned with literal fixtures, not
  ghRunner-dependent like resolveEntry/scanTodo/runScan.
- Narrow the 'never flagged stale' / 'is NEVER stale' claims (header
  fix (a), resolveEntry's docstring, and its inline comments) to what
  actually holds: the branch reads clean WHILE an OPEN PR exists. A
  reused branch whose new work has not yet opened a PR still falls
  through to the MERGED check and reads stale — a branch-name-identity
  limitation, not a bug, tracked in the TODO backlog for a durable PR
  identity fix. No behaviour change.
@mtskf
mtskf merged commit fb23929 into main Jul 29, 2026
1 check passed
@mtskf
mtskf deleted the chore/check-stale-todo-markers-false-verdicts branch July 29, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant