Skip to content

feat(ci): add .gitignore pattern test-coverage gate for #519 - #538

Merged
tvna merged 2 commits into
mainfrom
claude/gitapex-pr-519-vmcgc3
Jul 29, 2026
Merged

feat(ci): add .gitignore pattern test-coverage gate for #519#538
tvna merged 2 commits into
mainfrom
claude/gitapex-pr-519-vmcgc3

Conversation

@tvna

@tvna tvna commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the one remaining CI/pytest gate #519 asks for (#330: a newly-added .gitignore pattern must have a test asserting it). The other three findings #519 cites were already fixed by prior, unrelated work -- see Facts below -- so this PR does not touch them.

Facts

Verified against the current codebase, not just #519's restated retrospective facts (which predate later fixes):

  • grep -rn "check-ignore" . finds exactly one hand-written test (tests/test_gitignore_worktrees.py), scoped to a single hardcoded path -- no general "new pattern needs a test" gate exists anywhere in the repo.
  • tests/test_gitignore_worktrees.py:14-33 already uses git check-ignore -v and asserts the reported source resolves to REPO_ROOT/.gitignore (#347's finding -- already fixed, no -q exit-0 check remains).
  • .github/scripts/gate_skill_rename_lifecycle.py + tests/test_gate_skill_rename_lifecycle.py + .github/workflows/skill-rename-lifecycle-gate.yml already enforce spec.lifecycle.renamedFrom on a removed skill directory (#285's finding -- already fixed, wired on skills/**/SKILL.md diffs).
  • tests/test_check_acm_present_sync.py already discovers all 4 known check_acm_present.py-shaped copies and fails on a diverging header regex unless every diverging copy carries an "intentionally diverged" marker (#242's finding -- already fixed).

Assumptions

Risk / blast radius

New CI workflow only triggers on .gitignore diffs (paths: [".gitignore"]); no effect on any other PR. contents: read only, no write scopes.

Rollback

Delete .github/workflows/gitignore-pattern-coverage-gate.yml, .github/scripts/gate_gitignore_pattern_coverage.py, and tests/test_gate_gitignore_pattern_coverage.py; no other files touched.

Verification

Acceptance Criteria Map from #519, restated row-by-row:

Criterion Proof method Result
New .gitignore pattern needs a test (#330) Add a pattern with no test, confirm the gate fails; add a referencing test, confirm it passes Done. Live-proved directly against gate_gitignore_pattern_coverage.py: a scratch added.txt pattern with no referencing test file exits 1 (FAIL: ... has no test); adding a test file containing the pattern's literal text exits 0 (PASS: test coverage found for all 1 added pattern(s)).
Drift test asserts match resolves to this repo's own .gitignore (#347) Simulate an ambient exclude rule masking a deleted repo rule Already satisfied by tests/test_gitignore_worktrees.py (pre-existing, unchanged by this PR).
Renamed skill sidecar sets renamedFrom (#285) Rename without setting it, confirm fail; set it, confirm pass Already satisfied by gate_skill_rename_lifecycle.py + its workflow (pre-existing, unchanged by this PR).
Duplicate check_acm_present.py copies share header regex or carry divergence marker (#242) Diverge one copy without the marker, confirm fail; add marker, confirm pass Already satisfied by tests/test_check_acm_present_sync.py (pre-existing, unchanged by this PR).

Commands run locally:

  • uv run --frozen pytest tests/test_gate_gitignore_pattern_coverage.py -v -- 19 passed, 97% line coverage on the new script.
  • uv run --frozen pytest (full suite) -- 940 passed.
  • ruff check .github/scripts/gate_gitignore_pattern_coverage.py tests/test_gate_gitignore_pattern_coverage.py -- all checks passed.
  • python3 -c "import yaml; yaml.safe_load(open('.github/workflows/gitignore-pattern-coverage-gate.yml'))" -- valid YAML. (actionlint itself could not run in this sandbox -- no docker daemon -- so the new workflow was instead reviewed line-by-line against the already-CI-validated skill-rename-lifecycle-gate.yml it mirrors.)

Checklist

  • Tests pass locally
  • Docs updated if behavior changed (N/A -- CI-only change, no user-facing docs)
  • Issue number cited in every commit

Related Issue

Closes #519


Generated by Claude Code

Closes the one remaining gap from #519's retrospective triage (#330):
a newly-added .gitignore pattern with no test verifying it. Mirrors
gate_skill_rename_lifecycle.py's shape (script + pytest suite + a
merge-base-diff-driven workflow).

The other three findings #519 cites (#347, #285, #242) are already
fixed by prior work -- tests/test_gitignore_worktrees.py's existing
'git check-ignore -v' + source-file assertion, gate_skill_rename_lifecycle.py,
and tests/test_check_acm_present_sync.py respectively -- so this PR
does not touch them.

Refs #519
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

- find_offenders() used a raw substring check, so a pattern's core text
  matching inside an unrelated word (e.g. "build" inside "rebuild_step")
  reported an uncovered pattern as covered, defeating the gate. Bound
  the match with non-alnum/underscore lookarounds so the core must
  appear as its own token.
- main() only caught FileNotFoundError and left the file handle open on
  success; a directory path or permission-denied file crashed with a raw
  traceback. Use a context manager and catch OSError generally.

Refs #519
@tvna
tvna merged commit 52a0925 into main Jul 29, 2026
2 checks passed
@tvna
tvna deleted the claude/gitapex-pr-519-vmcgc3 branch July 29, 2026 04:04
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.

feat(ci): add repo-hygiene gates -- gitignore drift, skill-rename lifecycle, duplicate-script sync

2 participants