Repository navigation
Release evidence: compare "release-os", the OS release and exact tools but not the image version - #80
Conversation
…s but not the image version (#79) A third value of profiles.<name>.compare for evidence jobs whose result does not depend on the hosted image patch version, given exact tools, such as a sharded table that lands on several images during a staged roll. - Tools: every tool pinned (config-invalid otherwise); recorded tools equal the pins exactly, as under "pins". - OS release and platform: the release job resolves the profile's image name, OS, architecture and OS-release digest on its own runner, before any evidence is read, but not its tools; each job must report exactly those, and the jobs of a variant and profile must agree on them. A difference refuses runtime-identity (identity-changed between jobs) and is never revalidated: revalidation reruns the check list, which is not this work. - ImageVersion: recorded per job and as the release runner resolved it (image_versions and release_runner in the group record), compared with nothing. - Needs the identity policy (config-invalid without it); a variant with no OS-release file (windows-x64) is refused (identity-config). runtime() is split into runner_runtime() and the tools; the group rule moves into identity_group(). release-runner and pins decide as before: the existing tests pass unchanged. The vendored scripts/dev/release_evidence.py is copied again; its version line is unchanged. Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clean-room review (same vendor, not a cross-vendor claim) of #80 at 2707c9fScope
What was established from the code
Findings
No HIGH or MEDIUM finding. Observed and not filed (documented behaviour, fails closed): every Probes (over the repository's own
|
| Mutant | Result | Caught by |
|---|---|---|
| M01 the release-os comparison with the release runner removed | killed | ReleaseOS.test_another_os_release_refuses_and_is_never_revalidated, ReleaseOS.test_the_comparison_holds_every_runner_field_but_the_image_version, ReleaseOSCLI |
| M02 the OS-release digest left out of that comparison (platform fields only) | killed | the same three |
| M03 a release-os difference returned as a drift | killed | test_another_os_release_refuses_and_is_never_revalidated (revalidated instead of refused), the comparison test |
M04 the OS-release-file rule removed from identity_context |
killed | ReleaseOSConfiguration.test_a_variant_without_an_os_release_file_refuses |
M05 release-os accepted without identity |
killed | ReleaseOSConfiguration.test_release_os_needs_the_identity_policy_and_every_tool_pinned |
M06 the every-tool-pinned rule for pins only |
killed | the same (3 cases) |
| M07 the eager resolution without the variant (no form check of the release runner's version, the default OS-release file) | killed | five ReleaseOS tests |
| M08 the release-os group ignores the tools | killed | ReleaseOS.test_the_group_holds_every_field_but_the_image_version |
M09 the group record without image_versions |
killed | ReleaseOS acceptance, the group test, ReleaseOSCLI |
M10 the group record without release_runner |
killed | ReleaseOS acceptance and revalidation, the group test, ReleaseOSCLI |
M11 the job's ImageVersion form check removed (check_identity; under release-os the only hold on it) |
killed | ReleaseOS.test_a_platform_that_differs_refuses (shard latest), Identity.test_an_unexpected_identity_refuses_and_is_never_revalidated |
M12 the release runner's ImageVersion form check removed (runner_runtime) |
killed | ReleaseOS.test_a_platform_that_differs_refuses, IdentityAtCommit, IdentityCLI |
M13 the recorded tools not held to the pins under release-os |
killed | ReleaseOS.test_a_tool_that_is_not_its_pin_or_another_tool_set_refuses (the group rule refuses identity-changed instead of runtime-identity) |
M14 the tool set not held under release-os |
killed | the same, same way |
| M15 a lazy full resolution added beside the eager loop | survived: equivalent as constructed (the eager loop fills here first) |
replaced by M15b and M15c |
M15b the eager loop removed; the release runner resolves release-os lazily, tools included |
killed | ReleaseOS.test_a_platform_that_differs_refuses (run listing read), ReleaseOS acceptance (tools resolved), ReleaseOSCLI |
| M15c the eager loop removed; lazy per-job resolution without tools | killed | ReleaseOS.test_a_platform_that_differs_refuses, ReleaseOSCLI (workflows recorded) |
M16 the release-os group's identity-changed not raised in verify |
survived: equivalent through decide() (F2); the rule is held by the direct group test |
none needed |
| M17 the comparison over the release runner's keys only | killed | ReleaseOS.test_the_comparison_holds_every_runner_field_but_the_image_version |
M18 a pins group never refuses (regression of the existing mode) |
killed | Identity.test_legs_of_one_variant_and_profile_that_report_different_identities_refuse, ReleaseOS.test_pins_and_release_runner_still_refuse_the_same_roll, the group test |
Regression check beside the mutants: the differential above (340 base-branch decision records,
identical under the old and the new gate).
Verdict
merge: yes
Set JPDEV_VERSION to 0.15.0 and line 2 of templates/check.sh and of the vendored scripts/dev/check.sh, handover_check.sh, changed_checks.py and release_evidence.py to v0.15.0, and add docs/releases/v0.15.0.md for #79/#80, the release-os compare mode. Fix the three LOW documentation findings of #80's review in docs/ci-evidence.md: a job of another image name, OS or architecture refuses identity-unexpected, and only another OS-release digest refuses runtime-identity; the release-os group rule (identity-changed) is a backstop that the per-job checks pre-empt through verify; release_runner also holds the OS-release digest. Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
What changed
A third value of
profiles.<name>.comparein the release evidence gate,release-os, for evidence jobs whose result does not depend on the hosted image patch version, given exact tools. The motivating case is judgment-pack-agent#332: during a staged image roll, a 28-shard table landed on three image versions, and neitherrelease-runnernorpinscould pass.Under
"compare": "release-os":config-invalidotherwise). Each job's recorded tools must equal the pins exactly, as underpins.release-runner:ImageOS,RUNNER_OS,RUNNER_ARCHand the digest of the variant's OS-release file. It does not resolve the tools, because the pins hold them. This happens before any evidence is read, so a release runner of another platform or image name, or one without the OS-release file, refuses there (runner-platform,identity-unexpected,identity-missing). Each job must report exactly those values (runtime-identity). The jobs of one variant and profile must also agree on everything exceptImageVersion(identity-changed).image_versionsandrelease_runner. It is compared with nothing: not with the release runner, and not among the profile's jobs.config-invalid: ... compare "release-os" needs config.identity). A variant with no OS-release file (windows-x64) is refused for this mode (identity-config), at capture and at verify.A difference is a plain refusal, not a drift. An OS-release or platform difference refuses
runtime-identityat that job. It is never revalidated, even withrevalidation.allowed: true, and it wins over arelease-runnerdrift. Revalidation reruns thedev.jsoncheck list on the release runner. That stands in forrelease-runnerjobs because their work is that list. The work this mode is for (table shards, legs with emptychecks) is not that list, so arevalidatehere would publish with that work covered by nothing. This matches howrelease-runneralready treats what revalidation cannot cover (a mismatched pin, a record of another shape or platform). When arelease-runnerdrift alone decidesrevalidate, arelease-osjob's evidence is taken as apinsleg's is, and it is not inrevalidation.jobs.Code:
runtime()is split intorunner_runtime()(image and platform) plus the tools, with the same refusals in the same order. The group agreement rule moves intoidentity_group().check_runtime()gains therelease-oscomparison, andidentity_context()gains the OS-release-file rule.scripts/dev/release_evidence.pyis copied again. Its version line (v0.14.0) andJPDEV_VERSIONare unchanged; the release prep is a separate pull request.Public surface: this adds a configuration value and two decision-record members (
image_versionsandrelease_runneronrelease-osgroups only). The docs change in the same pull request: the compare and pins rows, the toolchain bullet, the runner identity comparison and image-roll cases, a new section "The release-os mode", the decision record and the revalidation bound indocs/ci-evidence.md; step 3 of the runner identity adoption indocs/adopting.md; and a comment intemplates/ci-evidence.json.What the mode gives up
It is weaker than
release-runner, and it is an explicit choice per profile:Pull-request-to-branch reuse is unchanged: the pull request job's runtime, image version included, must still equal the branch job's.
Tests
tests/release_evidence_test.pyadds 11 tests. All 167 existing tests pass unchanged; no existing line was edited.ReleaseOS(decide()with three table shards on three image versions, beside the Linux release-runner job and the macOS pins leg). The release runner's resolution of the table runs for real, from a patched environment and an OS-release file the test writes. The tests cover:release-runnerdrift;check_runtimetest covering each runner field other than ImageVersion;identity_grouptest;pins(identity-changed) andrelease-runner(runtime-identity, orrevalidate) still refusing the same roll, with the exact detail strings they had;ReleaseOSConfiguration: no identity policy, a missing pin (another tool, empty pins, no pins member), thepinswording unchanged, an unknown mode, and thewindows-x64variant refused atidentity_contextover a fixture git repository.ReleaseOSCLI:captureon one image version andverifyon another through the public command (fixture repository and a stand-ingh). The evidence is reused, refused for another OS release, and refused before any evidence is read for another image name.Differential check (scratch, not committed): every decision record produced by the 114 pre-existing tests that drive
decide()was compared under the old and the new gate. That is 340 records, and all were byte-identical. The same comparison against a mutant that changes thepinsgroup reported 5 tests with different records, so the comparison can see a change.Mutants. Each was applied to a scratch copy and run through the whole file via
RELEASE_EVIDENCE_HELPER. Each was caught at the intended assertion; no setup errors are counted.release-oswithoutidentityacceptedReleaseOSConfiguration.test_release_os_needs_the_identity_policy_and_every_tool_pinnedrelease-osnot in the every-tool-pinned ruleidentity_contextReleaseOSConfiguration.test_a_variant_without_an_os_release_file_refusesReleaseOS.test_a_platform_that_differs_refuses(the run listing was read),ReleaseOSCLI(workflows recorded)ReleaseOS.test_another_os_release_refuses_and_is_never_revalidated,test_the_comparison_holds_every_runner_field_but_the_image_version,ReleaseOSCLIReleaseOSacceptance, revalidation and comparison tests,ReleaseOSCLIReleaseOS.test_another_os_release_refuses_and_is_never_revalidated(revalidated), comparison testos_releasecompared (platform fields dropped)ReleaseOS.test_the_comparison_holds_every_runner_field_but_the_image_versionReleaseOSacceptance, revalidation and group testsReleaseOS.test_the_group_holds_every_field_but_the_image_versionruntime())ReleaseOSacceptance (tools resolved) and platform testspinsgroup treated likerelease-osIdentity.test_legs_of_one_variant_and_profile_that_report_different_identities_refuse,ReleaseOS.test_pins_and_release_runner_still_refuse_the_same_roll, group testrelease-osjobs counted inrevalidation.jobsReleaseOS.test_a_revalidation_takes_the_shards_evidencerelease-osremoved fromCOMPAREReleaseOSCLIidentity-changednot raisedIdentity.test_legs_of_one_variant_and_profile_that_report_different_identities_refuse,ReleaseOS.test_pins_and_release_runner_still_refuse_the_same_rollThrough
decide(), therelease-osgroup agreement is redundant with the per-job comparison: each job is already held to the release runner on every field other than ImageVersion and to the pins on its tools. Its rule is therefore caught by the directidentity_grouptest, not by adecide()vector.Checks
Candidate: head
2707c9f, no uncommitted diff. The checks ran on the working tree whose content is this commit's tree. Python 3.12.11.scripts/dev/check.sh, the whole list, once:The two failing cases in
testscame from the run environment, not the change: aTMPDIRspelled with a..segment (the generated-docs fixtures require a resolved directory), and a hardulimit -vthat kept the kit's Go wrapper test from changing its own cap. With a resolvedTMPDIRand a soft cap,scripts/dev/check.sh --only tests:tests/release_evidence_test.pyalone: 178 tests OK. They are also OK against the vendoredscripts/dev/release_evidence.py. CI owns the exact-head run.Closes #79
🤖 Generated with Claude Code