Presence does not identify the run
A report file can survive the failure that should have produced its successor. In the lab, run A writes a valid result. A new process exits seven without touching that file. A consumer asking only whether pass.json exists receives a positive answer but reads the wrong run. The local control compares runId against the expected run and returns run-mismatch. Retain A as useful history; do not change its field to B. In a real pipeline, separate directories or names by run and validate consumer inputs. This separation reduces leftovers but does not replace functional outcomes, origin and candidate identity.
Candidate identity
The lab calculates SHA-256 of an original text file named candidate.txt. It then changes bytes from A to B and compares the earlier report with the current digest. The mismatch produces artifact-mismatch even though the external filename remains unchanged. Comparison detects association with different bytes; it does not conclude that the change is malicious or incorrect. For delivery, preserve the link between revision, artifact, environment and outcomes, with the relevant producer and run. If the candidate must be rebuilt or replaced, assess which evidence remains applicable and which checks need repeating. Do not merely update the report digest as though that produced a new test execution.
The deliberate gate limitation
The final run passes the gate: runId matches, digest matches and both required IDs passed. Nevertheless, candidateExecuted is false. Tests use literals and a text conversion; the candidate file is only hashed. This difference is intentional and belongs in the learner conclusion. In a real project, record where the exercised module or service came from and verify that it corresponds to the delivery target. An older environment installation can answer tests while metadata points to a new candidate. This lab implements neither that installation nor complete traceability. It demonstrates the association limit, leaving proof of the real target as an additional representative exercise.
Transport, presentation and blocking
The local report is teaching JSON, not JUnit. In the additional GitLab scenario, official documentation distinguishes JUnit result presentation from job status: publishing failures is insufficient if the script exits successfully. Define how to retain diagnostics without losing producer outcome and confirm the condition blocking promotion. Artifact configuration also determines what the consumer receives; dependencies and needs:artifacts allow producer selection. If several components use report.json, the common name does not give them the same scope. Review producer, run and candidate before trusting the received input. These GitLab behaviors were studied in documentation; this lab performed no JUnit upload or external pipeline execution.
Trust and proportionate policy
runId and digest are declared fields, and the lab neither signs them nor verifies an external identity. A producer able to change everything freely can create self-consistent content. Real approval needs a protected evidence path and a risk-appropriate policy. Nor should the two fictional IDs be copied into every project. A Linux permission test may be inapplicable on Windows while reconciliation remains mandatory on both. Document exclusions by context, review inventory changes and retain earlier results. One legitimate exception does not authorize accepting all skips. Distinguish technical controls, risk decisions and owners so RUN knows when to resolve, repeat or escalate.
Handover exercise and summary
Prepare three packages for a workshop: report A presented for B, a mismatched digest and a correctly associated report whose tests did not load the candidate. Development identifies the exercised target; QA explains verification scope; RUN decides what is missing before proceeding. Add a variant with a visible JUnit report and a script exiting zero despite failures. Request a short note containing observation, impact, next step and owner. The human exercise has not been performed and roles are fictional. The automated lab executed thirteen local groups twice, with eleven processes per run and temporary-directory cleanup. Summarize: retain history, confirm context, verify the target and connect results to decisions.
python3 content/labs/cicd-results/run.py --output /tmp/dr-cicd-results-second.json
# old-report-survives-failed-producer: producerExitCode=7
# The old report still says successful=true; gate rejects run-mismatch.
# report-bound-to-other-artifact: gate rejects artifact-mismatch.
# fresh-association-limited-scope: local gate accepts, candidateExecuted=false.
# The JSON report is a teaching format, not JUnit or a signed attestation.
# Two local methods passed; no candidate application was executed.An old report remains in a workspace after the new run fails; another has the right hash, but its tests did not execute the candidate file.
Common pitfalls
Accepting existence or date as identity, rewriting runId, confusing JUnit publication with blocking or treating a digest as proof of execution and trust.
Related topics: Job contracts · Test evidence · Release management
Evidence must be current, applicable to the target and obtained from an appropriate producer. A readable, self-consistent file may still fail to support the decision.
Reference: Job artifacts · CI/CD practices 2026-09; scoped GitHub Actions GitLab Jenkins and Azure DevOps Services documentation