Skip to content

fix(tests): repair four guards that pass without checking what they claim - #949

Merged
mlorentedev merged 3 commits into
mainfrom
fix/guard-effectiveness
Aug 14, 2026
Merged

fix(tests): repair four guards that pass without checking what they claim#949
mlorentedev merged 3 commits into
mainfrom
fix/guard-effectiveness

Conversation

@mlorentedev

Copy link
Copy Markdown
Owner

Summary

Four independent fixes under one theme: a test or hook that reports green without exercising the thing it names.

  • stub-real-pairing misses suites that stub a binary from a sourced library #892stub-real-pairing.bats detected a stubbing suite structurally, but only by grepping the .bats file itself; a suite stubbing a binary from a sourced library (tests/vault-health-golden.bats via tests/golden/vault-health/lib.sh) evaded it entirely. Widened the detector to follow one level of ./source indirection under tests/, added the new exemption, and single-sourced the exemption list — which was itself already drifted (bitacora-rollout was exempted but missing from the stale-entries check, so a staleness regression there would have gone undetected).
  • BUG-054: the busy-binary install test never holds a binary busy, so the ETXTBSY swap path is untested #807 (BUG-054) — the busy-binary install test never actually held a binary busy: a coreutils multi-call sleep, copied to a file named dotf, exits immediately instead of sleeping. Confirmed empirically on this machine — uutils coreutils dispatches by resolved executable path, not argv[0], so the commonly-proposed exec -a fix does not help here either (see the lessons.md entry for the reproduction). Replaced the fixture with a copy of bash busy-spinning on shell builtins only — no multi-call dispatch to satisfy, so the ETXTBSY path is genuinely reached regardless of host coreutils flavor.
  • BUG-061: pre-push spec-gate false-negative on archive-on-merge (no PR body locally) #854 (BUG-061) — check-spec-gate.sh's archive-on-merge LOC credit only fires when SDD_PR_BODY names a closing keyword, which is empty on every local pre-push run by design — so a PR that correctly archived its spec in the same change could not be pushed locally. Added scripts/spec-gate-prepush.sh, a local wrapper mirroring the existing CI adapter (spec-gate-pr.sh) that resolves the branch's live PR via gh and falls through to today's behaviour on any resolution failure. Wired into .pre-commit-config.yaml's opt-in pre-push hook. An earlier attempt credited any local active-to-archived spec transition unconditionally; the repo's own #397 regression tests caught that this reopens the "gratuitous archive-move dodging the gate" bypass, since local and CI share the same script gated only by an env var — reverted in favor of the wrapper.
  • BUG-039: extensionless git-hooks miss .gitattributes eol=lf — CRLF shebangs disable the GUARD-001 dispatcher #761 (BUG-039) — git-hooks/** already carries an explicit eol=lf rule (BUG-068); the same CRLF hazard was uncovered for cli/internal/initrepo/templates/* (embedded into the dotf binary via go:embed, so a CRLF checkout ships a CRLF Makefile/gitignore/claude-md into every scaffolded repo), ssh/config, LICENSE, and the tests/golden/** fixture corpora. Added the missing .gitattributes rules and a class-level regression test asserting every tracked extensionless text file resolves an explicit eol, so the next one added outside a covered group fails loudly instead of depending on which OS happens to check it out.

Each fix is mutation-tested: the corresponding guard is shown failing against the pre-fix code/config and passing again after (details in the commit message and docs/lessons.md).

SDD skip rationale

Four independent, self-contained bug fixes, each individually a small obvious-cause fix backed by its own mutation-tested regression guard. They do not share one feature-id to scope a single spec around — SDD specs model a single feature/change, not a themed bundle of unrelated small fixes — so this ships under the skip-sdd escape hatch rather than forcing an artificial spec folder.

Test plan

…laim

Four independent bug fixes under one theme: a test or hook that reports
green without exercising the thing it names.

- stub-real-pairing.bats detected a stubbing suite structurally, but only
  by grepping the .bats file itself; a suite stubbing a binary from a
  sourced library (tests/vault-health-golden.bats via
  tests/golden/vault-health/lib.sh) evaded it entirely. Widened to follow
  one level of `.`/`source` indirection under tests/, added the new
  exemption, and single-sourced the exemption list -- which was itself
  drifted (bitacora-rollout was exempted but missing from the
  stale-entries check). (#892)

- The busy-binary install test never actually held a binary busy: a
  coreutils multi-call `sleep`, copied to a file named `dotf`, exits
  immediately instead of sleeping. Confirmed on this machine (uutils
  coreutils dispatches by resolved executable path, not argv[0], so the
  commonly-proposed `exec -a` fix does not help here either). Replaced
  the fixture with a copy of `bash` busy-spinning on builtins only -- no
  multi-call dispatch to satisfy, so the ETXTBSY path is genuinely
  reached regardless of host coreutils flavor. (#807)

- check-spec-gate.sh's archive-on-merge LOC credit only fires when
  SDD_PR_BODY names a closing keyword, which is empty on every local
  pre-push run by design -- so a PR that correctly archived its spec in
  the same change could not be pushed locally. Added
  scripts/spec-gate-prepush.sh, a local wrapper mirroring the existing CI
  adapter (spec-gate-pr.sh) that resolves the branch's live PR via `gh`
  and falls through to today's behaviour on any resolution failure.
  Wired into .pre-commit-config.yaml's opt-in pre-push hook. (An earlier
  attempt credited any local active-to-archived spec transition
  unconditionally; the repo's own #397 regression tests caught that it
  reopens the "gratuitous archive-move dodging the gate" bypass, since
  local and CI share the same script gated only by an env var.) (#854)

- git-hooks/** already carries an explicit eol=lf rule (BUG-068); the
  same CRLF hazard was uncovered for cli/internal/initrepo/templates/*
  (embedded into the dotf binary, so a CRLF checkout ships CRLF
  Makefile/gitignore/claude-md into every scaffolded repo), ssh/config,
  LICENSE, and the tests/golden/** fixture corpora. Added the missing
  .gitattributes rules and a class-level regression test asserting every
  tracked extensionless text file resolves an explicit eol, so the next
  one added outside a covered group fails loudly. (#761)

Each fix is mutation-tested: the corresponding guard is shown failing
against the pre-fix code/config and passing again after.

Closes #892
Closes #807
Closes #854
Closes #761
@mlorentedev mlorentedev added the skip-sdd SDD spec-gate escape hatch (requires '## SDD skip rationale' in PR body) label Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Copilot, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 100 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e87c4111-fe57-4646-a428-860cd9e69aa5

📥 Commits

Reviewing files that changed from the base of the PR and between 18ccd60 and 44101dd.

📒 Files selected for processing (12)
  • .gitattributes
  • .pre-commit-config.yaml
  • docs/lessons.md
  • scripts/check-spec-gate.sh
  • scripts/spec-gate-pr.sh
  • scripts/spec-gate-prepush.sh
  • tests/gitattributes-eol.bats
  • tests/install-dotf.bats
  • tests/spec-gate-adjacency.bats
  • tests/spec-gate-prepush-real.bats
  • tests/spec-gate-prepush.bats
  • tests/stub-real-pairing.bats

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

check-spec-gate.sh's ADJACENCY_ISSUES comment and spec-gate-pr.sh's header
both described the pre-push tier as staying entirely offline, citing #854
as the reason. That framing predates this PR's fix: the pre-push tier now
gets live PR context through scripts/spec-gate-prepush.sh, a separate
wrapper that needs no token. Reworded both comments so they describe what
still holds (this script fetches nothing itself; the CI adapter needs
GH_TOKEN because it reads a PR it does not own) instead of a claim the
same PR just made no longer true.
@mlorentedev

Copy link
Copy Markdown
Owner Author

Cross-session note from the branch working on BUG-074 (PR #950).

  1. This PR is CONFLICTING (mergeStateStatus=DIRTY). docs: truth pass — README, ADR hygiene, architecture map, cli/README, opencode split #945 (docs/truth-pass-batch) merged into main mid-flight, so this branch is behind. git fetch origin && git rebase origin/main. Expect the collision in docs/lessons.md — every parallel branch this sprint appends there, which is the conflict class already recorded in that same file from PR docs(lessons): continuing a squash-merged branch reopens the original diff #936. Keep both entries.

  2. CodeRabbit reports pass with the description "Review rate limited" — it never read this diff. Four parallel PRs exhausted the free-tier quota; docs: truth pass — README, ADR hygiene, architecture map, cli/README, opencode split #945 consumed it and the other three got a green check from a reviewer that never ran. Evidence on CI: CodeRabbit's free-tier quota silently skips review on some PRs, and the merge gate cannot tell #906.

  3. Relevant to this PR's own scope: BUG-061: pre-push spec-gate false-negative on archive-on-merge (no PR body locally) #854 (BUG-061) just fired for real, twice. ./scripts/check-spec-gate.sh passed locally on both fix(harness): converge the deploy engine — prune, drift detection, all six surfaces #948 and fix(doctor): prove the live secrets SSOT by reach, not by PATH presence (BUG-074) #950, then CI's scripts/spec-gate-pr.sh failed both with an archive-on-merge violation — because the local script cannot see the PR body and so cannot evaluate closing keywords. That is the false negative the ticket describes, now reproducible on demand rather than hypothetical. Good material for the regression test that fix needs.

@mlorentedev

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

@mlorentedev

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: mlorentedev <13562150+mlorentedev@users.noreply.github.com>

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved and pushed in 44101dd.

@mlorentedev
mlorentedev merged commit e5a2ecc into main Aug 14, 2026
13 of 14 checks passed
@mlorentedev
mlorentedev deleted the fix/guard-effectiveness branch August 14, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-sdd SDD spec-gate escape hatch (requires '## SDD skip rationale' in PR body)

Projects

None yet

2 participants