Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
FREEDOM251

Freedom251 › Blog › CI/CD

How to Build a Fixture Contract and Merge Packet When CI Is Green

By Freedom251 Team · · 8 min read

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A green required check is evidence that a configured job completed successfully; it is not proof that a fixture change was explained, a local hook ran, or a retry did not hide an earlier failure. A practical safeguard is a small, auditable contract: hash committed fixtures into a manifest, require a human note for every intentional change, check that contract before push and again in CI, then publish a JSON merge packet whose merge_ready value is calculated by repository code.

Emery Lin’s formulation is precise: “A green required check is not a merge decision.” The workflow below turns that warning into reviewable evidence without pretending to be an auto-merge system.

As an Amazon Associate I earn from qualifying purchases.

What a green CI result actually tells you

Required checks report the conclusion of the jobs your branch protection policy knows about. They do not automatically tell reviewers whether generated fixtures moved for a valid reason, whether a developer bypassed a local hook, or whether a retried job replaced an earlier failure without preserving the history. Those are review and provenance questions, not merely execution results.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The distinction matters most for golden data: recorded HTTP bodies, GraphQL responses, serializer snapshots, and similar files that tests compare byte-for-byte. A test can pass after an accidental rewrite because the expected file was changed along with the implementation. The test result is green, while the explanation for the data change is missing.

The contract described here adds context around the status check. It does not replace code review, ownership rules, or protected branches. It gives reviewers a deterministic answer to two narrower questions: which fixture files changed, and were those paths explained?

Define a fixture contract

Choose and pin the golden-data tree

Start with one committed directory whose contents have review significance, such as test/fixtures or a service-specific recorded-response directory. Document which generated files belong to the contract. If a repository has several independent trees, define separate contracts or a deliberate sharding scheme rather than silently mixing unrelated data.

Generate a manifest by hashing every file in a stable path order. The path must be part of each manifest record; otherwise two files with identical bytes could be swapped without detection. A simple, adaptable shell sketch is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
find test/fixtures -type f -print0 | sort -z | xargs -0 sha256sum > test/fixtures.manifest.new

Use the repository’s existing hashing tools and line format if they are already standardized. The important properties are deterministic ordering, a committed baseline, and a reviewable diff. When content intentionally changes, commit the new manifest in the same change as the fixture edits.

Require a human explanation note

Alongside the manifest, keep a note file in a predictable location, for example fixtures-change.md. The note should name every changed fixture path and explain why it changed. A useful entry identifies the behavior under test, the data difference, and why the new value is expected. For example, an illustrative note might identify a checkout response fixture and say that a tax field was added because the API contract now returns it. That example is a format demonstration, not evidence that every checkout fixture should contain a tax field.

Do not treat a general sentence such as “updated snapshots” as sufficient. The checker needs a path-level relationship between the Git diff and the note. Teams can choose Markdown, YAML, or another format, provided the parser and review convention are stable.

Catch drift before the push

Install a repository hook directory

Keep hooks in version control so every contributor can use the same script. Configure Git once in the repository:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
git config core.hooksPath .githooks

Create .githooks/pre-push as an executable script. It should call repository-maintained commands that:

  1. Recompute the fixture manifest in the same stable order used to create the committed file.
  2. Fail if the working tree or generated manifest differs from the committed manifest in a way the change does not include.
  3. Collect fixture paths changed by the push and compare them with paths named in the explanation note.
  4. Print the first unexplained paths and exit nonzero so the push is rejected.

Keep the path-matching logic explicit. A naive substring check can confuse fixtures/user.json with fixtures/user.json.bak, and monorepos may have several roots with similar names. Normalize separators, compare repository-relative paths, and decide how renames, deletions, symlinks, and generated directories are handled. The source pattern is intentionally a sketch; adapt it to your tree before relying on it.

Understand the bypass

Local hooks are advisory because a contributor can push with git push --no-verify, disable the configured hooks path, or use a client that does not run the script. That is why the same contract must run in CI. The local check is still valuable: it catches accidental drift while the author can fix it, rather than waiting for a remote job.

Enforce the same contract in CI

Run the manifest and note checks in a dedicated contract job. The job should check out the commit under review, execute the repository scripts, inspect a commit trailer that records the hook signal if your team uses one, gather changed fixture paths, and write the merge packet.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the platform’s current, supported action versions when implementing the workflow; the versions shown in example configurations are not permanent guarantees. The important policy is that the contract job itself is required by the protected branch rule. An optional artifact upload or informational check can be bypassed; a required contract conclusion cannot.

Upload evidence even on failure

Configure the artifact step to run regardless of the contract result (for example, with the platform’s equivalent of an always condition). A failed gate with no artifact leaves reviewers guessing. The packet should instead show the failed conclusion, the paths that were found, and the missing explanations so the author can correct the commit.

Treat the hook trailer as a signal, not proof

A commit trailer can indicate that a local hook reported success, but anyone who can create the commit can forge that text. Make a missing trailer a warning by default if local development environments vary. A release branch may adopt a stricter rule that blocks when the trailer is absent. Neither policy turns the trailer into cryptographic provenance; use signed commits and protected branch controls when provenance is a requirement.

Design the merge packet

Store a machine-readable merge-packet.json as a CI artifact and expose the same information in the job summary when useful. Include enough identity and evidence for a reviewer to reproduce the decision:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Field What it records Why it matters
commit SHA, ref, and repository identity Links the packet to the exact revision that was checked.
hook Trailer presence and parsed hook state Shows whether the local signal was present, while preserving its non-attestation status.
fixtures.changed Repository-relative fixture paths in the diff Defines the review scope.
fixtures.unexplained Changed paths not named by the note checker Provides the blocking condition directly.
jobs Required-job names and conclusions Lets reviewers see which required results were considered.
merge_ready Boolean computed by repository code Gives automation one deterministic decision field.
explanation Optional short, human-oriented summary Adds context without becoming the source of truth.

An illustrative packet shape is:

{
  "commit": {"sha": "7f3c2b1", "ref": "refs/pull/42/head"},
  "hook": {"trailer_present": true, "status": "passed"},
  "fixtures": {
    "changed": ["test/fixtures/checkout.json"],
    "unexplained": []
  },
  "jobs": [{"name": "fixture-contract", "conclusion": "success"}],
  "merge_ready": true
}

The values above are illustrative. Your packet should use the actual commit, paths, and conclusions produced by the run.

Compute readiness in repository code

Keep the readiness rule small enough to audit. In the guide’s sample, readiness is true only when the contract job succeeds and no fixture paths remain unexplained:

contract_ok = packet['jobs_by_name']['fixture-contract']['conclusion'] == 'success'
unexplained_ok = len(packet['fixtures']['unexplained']) == 0
packet['merge_ready'] = contract_ok and unexplained_ok

That sample job list is a stub, not a complete CI aggregator. If readiness must include sibling workflows, query the platform’s live Checks API (or its equivalent) and map every required job to its current conclusion. Do not infer a green result from a partial list, a stale artifact, or a model-generated sentence.

Keep the decision code in the repository or a reviewed build script. A reviewer can then inspect exactly which conditions produce false. If the packet cannot be generated, fail closed: a missing decision is not permission to merge.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use model-generated prose only as an explanation

An optional summarizer can receive the packet and fixture note and produce a short brief listing blocked conditions, changed fixtures, and the jobs shown in the artifact. Give it a constrained instruction: report only supplied fields, do not invent job names, and never recommend merging. The model must not set merge_ready or override a failed contract.

If summarization fails, leave the brief null and retain the packet’s decision. If a brief says “looks good” while merge_ready is false, ignore the prose. Some teams should omit the model entirely if a plausible but incorrect sentence beside a correct machine decision would create unacceptable confusion.

Apply the decision rules during review

Packet condition Reviewer action
Manifest matches, note unchanged, and required jobs are green Proceed with normal review and ownership checks.
Changed fixture paths are named in the note Read the explanation and verify that the behavior change warrants the new data.
Any unexplained fixture path exists merge_ready is false; block until the note or fixture is corrected.
Contract job is red Block and inspect the packet; do not treat unrelated green jobs as a substitute.
Hook trailer is missing Warn under the default policy, or block on release branches that require it.
Model brief is missing Make no change to readiness; use the structured packet.
Brief is positive but packet is false Trust the packet and repository rule, not the prose.

Know where this pattern stops

It is not a merge queue

The contract does not serialize competing merges, rebase a branch, or guarantee that the checked commit is still current when a merge occurs. Use merge-queue controls when ordering and freshness are the problem.

Large trees need bounded work

Hashing a very large fixture tree on every push can consume substantial time. Shard manifests by service, or limit hashing to files Git identifies as changed while preserving a periodic full verification. Document the trade-off so a partial check is not mistaken for whole-tree coverage.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Screenshot fixtures can be noisy

Pixel snapshots often change because of fonts, rendering engines, timestamps, or platform differences. The contract is usually clearer for structured golden files. If screenshots remain in scope, normalize known nondeterminism and require reviewers to inspect the rendered diff, not just the hash.

Path matching is repository-specific

Generated paths, renames, submodules, and monorepo boundaries can defeat a simplistic note parser. Add tests for those cases and make the checker fail safely when it cannot classify a path.

Skip duplication when stronger controls already exist

If your organization already has a merge queue, signed status checks, and CODEOWNERS-based fixture review that cover the same risks, adding this packet may create duplicate policy without new evidence. This design is a lightweight contract for teams that lack that combination; it is not an auto-merge workflow.

A practical rollout sequence

  1. Inventory the fixture directories whose changes require explanation.
  2. Choose a stable manifest format and commit the initial hashes.
  3. Write the note convention and test the path checker against normal edits, renames, and generated files.
  4. Install the versioned pre-push hook and document the core.hooksPath setting.
  5. Re-run the same scripts in a required CI contract job.
  6. Generate and always upload the merge packet, including failed runs.
  7. Compute merge_ready from structured conclusions and unexplained-path count in reviewed repository code.
  8. Add stricter trailer or signing requirements only where the branch’s provenance policy needs them.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CI/CDcode reviewdeveloper workflowsGitTest fixtures

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.