Skip to content

tech-debt(guards): the exec-bit baseline cannot detect an entry that outlived its fix, and its tests broke when the backlog hit zero #15762

Description

@mrveiss

Found while fixing a CI red on #15752, which #15750's exec-bit restoration caused.

Two defects, one root

pipeline-scripts/check_hook_exec_bits.py keeps a shrink-only baseline, _KNOWN_DORMANT, and states the rule in its own comment: "This list only ever shrinks. A hook comes off it in the same change that fixes its violations and flips its exec bit."

1. The guard cannot detect a stale entry. tools/lint/check_no_blocking_io_in_async.py was executable on Dev_new_gui (mode 100755) while still listed as dormant. The staleness test that exists — test_the_dormant_baseline_has_no_stale_entries — asks whether a baselined path is still some hook's entry, which it was. Nothing asks whether it is still a violation. So an entry survives its own fix indefinitely, and the baseline overstates the backlog with no signal.

2. Two of the guard's own tests used the live baseline as their fixture, so finishing the backlog broke them:

  • test_a_known_dormant_entry_is_reported_but_not_blocking did next(iter(guard._KNOWN_DORMANT)) — StopIteration on an empty baseline.
  • test_running_from_a_subdirectory_does_not_report_clean asserted known was non-empty, which held only while the repository still carried a violation to classify.

#15750 restored the exec bit on tools/lint/check_no_local_schemas.py — the last live entry. The baseline emptied, and both tests failed. Not because the guard regressed, but because it succeeded. A guard whose tests stop working when its backlog reaches zero punishes finishing the work, and the cheapest way to make CI green again would have been to re-break the exec bit.

Fixed in #15752, and what is left

Both tests now use synthetic fixtures (monkeypatched _KNOWN_DORMANT, fabricated mode maps), so they assert the guard's actual properties whether or not a backlog exists. The CWD test asserts both directions — anchored run classifies, CWD-relative run loses it — and is mutation-checked: breaking the root anchoring fails it.

_KNOWN_DORMANT is now empty, both entries removed: one because this change fixed it, one because it had been fixed earlier and never recorded.

What is not fixed, and is this issue:

  • A test asserts that every _KNOWN_DORMANT entry is still an actual violation, so an entry cannot outlive its fix. This is the shrink-side check the existing staleness test does not perform
  • The same question asked of the repo's other shrink-only baselines — hardcoded_values_baseline.txt, the file-size ratchet pair — since "entry that no longer describes a violation" is a shape, not an instance
  • A convention recorded for guards whose backlog can reach zero: the test fixture must be synthetic, never the live baseline. This is the second time a ratchet's own suite has depended on the backlog it exists to eliminate

Related

Activity

  1. mrveiss commented on Sep 5, 2026

    @mrveiss
    OwnerAuthor

    Audit of the first acceptance criterion — asking the same question of the repo's other shrink-only baselines. Read-only; no change proposed here.

    A generic staleness guard already exists, and it catches a different shape

    repo_tests/stranded_recorder_entries_test.py (#15566) discovers 34 Python ledgers automatically — from KNOWN_LARGE's 499 rows to three-entry lint allowlists — plus registered text/JSON baselines, and fails on entries the tree no longer holds.

    It does not catch this issue's defect, and it says so itself:

    The recorded path is not in the tracked tree and not on disk. Nothing weaker: a count that no longer matches or a name that no longer resolves are the same class, but they are per-recorder semantics, and a guard that tried to know all of them would know none of them well.

    That is a deliberate, correct scoping decision, and it means the two shapes need to be named separately:

    Detected by Example
    Stranded — the recorded path no longer exists stranded_recorder_entries_test.py a baselined file that was deleted or moved
    Outlived — the path exists but no longer describes a violation nothing tools/lint/check_no_blocking_io_in_async.py, executable on base while still listed as dormant

    So this issue is not a gap in #15566; it is the per-recorder half that #15566 declined to guess at. The check has to be written per recorder, and the exec-bit baseline is the first instance.

    The escalation: on a tolerance list, an outlived entry is an open door

    I filed this as tidiness. It is worse than that, and the distinction is what a recorder is for:

    • A record with an outlived entry overstates a backlog. Misleading, not dangerous.
    • A tolerance list with an outlived entry silently exempts that path forever.

    _KNOWN_DORMANT is the second kind: (known if target in _KNOWN_DORMANT else blocking). A listed path never blocks. So if check_no_blocking_io_in_async.py had lost its exec bit again after being fixed, the guard would have classified the fresh violation as "known dormant and tracked in #14181" and passed — a genuine regression, on a path already fixed once, permanently invisible.

    That risk is gone for this list because it is now empty. The shape is not: every tolerance-shaped ledger in the repo has it, and stranded_recorder_entries_test.py cannot see any of them, because the exempted path still exists — that is the whole point of it being exempted.

    Revised acceptance criteria

    Related

  2. modified the milestones: Backlog, v0.12.0 on Sep 12, 2026
  3. mrveiss commented on Sep 20, 2026

    @mrveiss
    OwnerAuthor

    Re-verification against current origin/main (2026-09-20). Tracked branch issue-15743-changepw is already gone from origin.

    Confirmed unimplemented — 0 of 3 ACs:

    • A test asserting every _KNOWN_DORMANT entry is still an actual violation (not just still configured as a hook) — checked pipeline-scripts/check_hook_exec_bits_test.py's full test list; test_the_dormant_baseline_has_no_stale_entries and test_the_staleness_check_would_catch_a_dead_entry exist, but per this issue's own description these ask "is it still some hook's entry", not "is it still executable" — no new test matches the gap. _KNOWN_DORMANT is currently empty (confirmed, per fix(auth): change-password gates on caller identity, closing the account-takeover path #15752), so there's nothing live to check against right now, but the missing mechanism itself wasn't added either — a synthetic fixture could still exercise it.
    • The same shrink-side check for the other named shrink-only baselines (hardcoded_values_baseline.txt, the file-size ratchet pair) — no matching test found in either area.
    • A documented convention that a guard's own test fixtures must be synthetic, never the live baseline, for any guard whose backlog can reach zero — searched docs/developer/*.md and the guard's own test file for this rule; not found.

    Nothing here contradicts the issue's own "Fixed in #15752" section (which I did not re-verify, since it's explicitly marked already done) — this comment covers only the three items the issue itself lists as NOT yet fixed. No branch exists to build on. Leaving open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions