CI Checks Pass Without Testing Files: Test the Checker

CI checks can pass without testing files when their target selection is empty or a wrapper hides the underlying error. Our original local exercise makes both problems visible. Across eight comparisons, a deliberately flawed checker produced three false passes. A revised checker rejected the three known violations in the selected fixtures.

This is a small teaching experiment for Nigerian coding learners and project reviewers. It does not estimate how often real CI systems fail, certify software security or evaluate an AI product. The code, expected outcomes, raw receipts and downloadable tables make the result inspectable.

Why a green badge needs an evidence contract

Microsoft’s 2 November first-party account of building Copilot Cowork describes checks that appeared to succeed without doing their intended work. Reported mechanisms include a pipeline exposing its final display command’s success, a formatter receiving no files and a changed-file list becoming empty after commit.

The team’s resulting practice asks a check to report its file count and demonstrate an intentional failure. The account also describes four blocking issues people found after agent reviewers missed them. These are reported team experiences, not a controlled study.

Its headline—nine engineers merging 1,211 pull requests on 21 October—is context, not a productivity target for a beginner group. Our experiment below uses an invented repository and one simple rule. It does not attempt to reproduce that team’s software, workload or development scale.

For your own project, define what the badge should establish. Name the target files, the rule, the comparison point and the expected response to a known violation. Then retain evidence that the actual check met that contract.

How CI checks pass without testing files

Our underlying checker looks for a fictional text marker, FORBIDDEN_MARKER, in selected text files. Finding the marker returns exit status 1. A clean file returns 0. This narrow rule gives the exercise a known answer without introducing a real vulnerability or private dataset.

The flawed wrapper selects files with plain git diff and sends the checker’s output through a final display command. Under the recorded Bash settings, that wrapper reports success even when the checker fails. Separately, its file selection misses staged changes and changes that have already been committed.

The official Git diff documentation explains why comparison points matter. Plain git diff compares working-tree changes with the index. Comparing against a named commit asks a different question. Therefore, “changed files” is incomplete unless you specify the endpoints and state being compared.

Our revised wrapper compares the working state with a fixed baseline commit, reports the observed file count and preserves the checker’s exit status. It also rejects an unexpectedly empty selection while permitting an explicitly declared empty case to return “not applicable.” This is a teaching implementation, not a universal production CI recipe.

Four controlled scenarios, eight executed comparisons

We created a new isolated fictional repository for each scenario. Each began with the same baseline text file. The experiment then changed that file and, where required, staged or committed the change. No existing project, remote repository or production CI setting was modified.

ScenarioTarget stateExpected applicable filesExpected result
S1Valid change, unstaged1True pass
S2Known marker violation, unstaged1Detected violation
S3Same kind of violation, staged1Detected violation
S4Same kind of violation, committed1Detected violation

We ran each scenario through both checker revisions, giving four times two equals eight comparisons. The retained manifest records baseline and head commit IDs, working status and the target file’s hash. An unchanged head in an uncommitted scenario does not mean that the working file was unchanged.

Eight executed comparisons across four synthetic scenarios. The flawed wrapper truly passes the valid case but falsely passes all three violation cases. The revised wrapper truly passes the valid case and detects all three violations.
These outcomes describe deliberately chosen fixtures. They do not establish a population false-pass rate or prove that the revised checker catches every possible problem.

The main comparison uses Python 3.10.10, Bash 3.2.57 and Apple Git 2.54.0 on one local macOS computer. We retain the exact environment labels in a CSV. Other shells, wrapper settings and CI environments can behave differently, so verify your own execution path.

Count the files that reached the checker

The flawed wrapper inspected one target file in S1 and S2. It inspected zero in S3 and S4. Nevertheless, those two empty selections returned success because the underlying toy checker found no marker in the empty input set.

Expected applicable-file count is one in each main scenario. The flawed wrapper observes one, one, zero and zero files; the revised wrapper observes one file in every scenario.
A reported file count exposes two empty target selections. This is selection coverage for the exercise, not source-code line coverage or defect coverage.

This distinction matters when presenting project evidence. A statement such as “the changed file passed” needs proof that the changed file reached the relevant rule. A successful process that examined nothing does not supply that proof.

Unlock Your Unique Tech Path

Get expert tech consulting tailored just for you. Receive personalized advice and solutions within 1-3 business days.

Get Started

Also distinguish a legitimate empty set from an unexpected one. A documentation-only change might properly have no files for a language-specific checker. In that situation, a declared NOT_APPLICABLE result is more informative than silently presenting the target rule as passed.

We ran a separate control after restoring the target file to its baseline content. With the explicit allow-empty policy, the revised wrapper returned NOT_APPLICABLE and exit 0. That control is retained in the pack but is not one of the eight main comparisons.

Preserve the error through the wrapper

S2 shows a different problem. The underlying checker inspected one file and returned 1 for the known violation. The flawed wrapper still returned 0. Its final display operation succeeded, concealing the earlier failure from the wrapper’s caller.

In S2, the flawed checker’s underlying process returns one but its wrapper returns zero. The revised checker and wrapper both return one for the same known violation.
Exit codes are process states, not severity scores. Retaining both values reveals an error that the final wrapper status alone would hide.

The revised teaching wrapper saves the rule’s status immediately, prints a short receipt and exits with that saved status. Review the source before adapting it. A production wrapper also needs an explicit policy for selection-command failures, deleted files, unusual paths and infrastructure errors.

Do not fix a failing gate by merely suppressing the failure or removing the target. First establish whether the failure represents a real rule violation, a legitimate skip or a problem in the test harness. Each requires a different response.

Three false passes are a fixture result, not a benchmark

Across the four chosen scenarios per revision, the deliberately flawed wrapper produces three false passes and the revised wrapper produces zero. One false pass masks an error; two involve empty target selection.
The counts are original local observations. Zero observed false passes in these selected cases does not prove correctness, security, comprehensive coverage or hiring readiness.

A larger-looking percentage would add little here. We deliberately chose cases that expose particular failure mechanisms. Calling three out of four a general CI failure rate would misrepresent that selection.

The experiment’s useful result is the trace: target selection, observed file count, underlying status, wrapper status and known expectation. A reviewer can inspect that trace and identify why the outcome differs between revisions.

Our GitHub Actions brownout audit addresses a different distinction: a policy-blocked attempt may create a failed run without executing jobs. Together, the guides show why neither a red nor a green badge explains the failed or successful layer by itself.

Run the fixture and review the evidence

Extract the downloadable pack into an empty folder. With Python 3 and Git installed, run python3 scripts/run_experiment.py from the extracted root. The harness invokes /bin/bash and creates new isolated repositories inside its output directory. It uses no network connection, credentials, paid API or real customer data.

Read the deliberately flawed wrapper as teaching material; do not install it as a production gate. The pack’s README lists the expected outcomes and limitations. Keep the supplied results as a baseline before rerunning or extending the exercise.

Four-part checker evidence contract: target selection and file count, preservation of the underlying failure, an explicit empty-set policy and a conclusion bounded to the tested rule and fixture.
Use the framework to review what a badge actually establishes. Selection coverage, line coverage and defect detection answer different questions.

For an existing project, start with a check you own and a harmless, reversible known violation. Record the expected outcome before running it. In a study group, have another person follow the instructions and report any undocumented step. We have not claimed independent replication of this local exercise.

For broader project organization, see our version control and automation guide. Keep this article’s narrower question—whether a check inspected and rejected the intended input—separate from general pipeline setup.

Download the results and original code

Download the runnable checker fixture pack (ZIP). It includes the original scripts, raw receipts, stdout/stderr records and README. Original synthetic data are CC0; original teaching code uses the MIT license included in the pack. Microsoft and Git materials retain their own rights.

Research and execution checked 7 November 2026. The main ledger contains eight selected local comparisons. A separate legitimate-empty control is labeled separately. These results do not measure Nigerian adoption, general software quality, AI accuracy or production cost savings.

Unlock Premium Source Code for Your Projects!

Accelerate your development with our expert-crafted, reusable source code. Perfect for e-commerce, blogs, and portfolios. Study, modify, and build like a pro. Exclusive to Nigeria Coding Academy!

Get Code