diff --git a/.github/workflows/binary-release.yml b/.github/workflows/binary-release.yml index e10f3d7d6e..7b626770b0 100644 --- a/.github/workflows/binary-release.yml +++ b/.github/workflows/binary-release.yml @@ -53,6 +53,10 @@ name: Binary Release # - `release: published` — fires automatically on each tagged release # - `workflow_dispatch` — manual re-run / backfill, takes a tag input # +# All the other workspace [[bin]]s (G5, #4189): `build-all-bins` ships each one, derived by +# scripts/nightly_manifest.py bins exactly as nightly.yml does, as --.tar.gz for +# x86_64/aarch64 gnu, each checked by asset_version_check.sh under its own name before upload. +# # pv targets: 4 Linux (x86_64/aarch64 × musl/gnu), each built natively on # the box of its architecture (x86_64 on yoga, aarch64 on gx10): gnu inside # rust:1.93.0-bullseye, musl inside rust:1.93.0-bookworm. musl variants are static and ideal for Docker / @@ -278,6 +282,178 @@ jobs: | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' done + # G5 (#4189): every other workspace [[bin]] on every tag. The set is the nightly's, derived by + # `scripts/nightly_manifest.py bins` from `cargo metadata` of the TAG tree -- never a hand list, so a + # new [[bin]] ships on the next tag with no edit here. apr and pv are the only bins left out: their + # dedicated lanes above ship them (cuda/cpu/darwin apr, musl+gnu pv), and a second `pv--` + # from this lane would clobber the pv lane's asset of the same name. gnu only, inside + # rust:1.93.0-bullseye (the 2.31 floor), natively on the box of each architecture as the pv lane. + # Each bin must answer `--help`, and its `--version` must print ` ()` -- + # asset_version_check.sh with the bin's own name, the check verify-apr-assets makes of apr -- + # before anything is uploaded; nightly_manifest.py smoke then gives the nightly's verdicts on the + # same executables. Assets are `--.tar.gz` + .sha256: they carry the tag once, so + # promote_rc.sh renames them for a final like every other asset. + build-all-bins: + name: all [[bin]]s ${{ matrix.target }} on ${{ matrix.host }} + needs: assets + if: needs.assets.outputs.present == 'false' + runs-on: ${{ fromJSON(matrix.labels) }} + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + host: yoga + labels: '["self-hosted", "Linux", "X64", "cuda", "yoga"]' + - target: aarch64-unknown-linux-gnu + host: gx10 + labels: '["self-hosted", "Linux", "ARM64", "cuda", "gx10"]' + steps: + - name: Resolve release tag + id: tag + run: | + TAG="${{ github.event.release.tag_name || inputs.tag }}" + if [ -z "$TAG" ]; then + echo "::error::No tag resolved (release event missing tag_name and no dispatch input)" + exit 1 + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + - name: Checkout at tag + uses: actions/checkout@v7 + with: + ref: ${{ steps.tag.outputs.tag }} + + # As every lane: the stamper and the checker come from THIS workflow's commit, not the tag tree. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + git show "FETCH_HEAD:scripts/release/asset_version_check.sh" > "$RUNNER_TEMP/asset_version_check.sh" + git show "FETCH_HEAD:scripts/nightly_manifest.py" > "$RUNNER_TEMP/nightly_manifest.py" + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + git show "FETCH_HEAD:scripts/release/stamp_rc_version.sh" > "$RUNNER_TEMP/stamp_rc_version.sh" + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + + - name: Preflight - this box has the tools this job assumes + run: bash scripts/ci_self_hosted_preflight.sh --need docker objdump + + - name: Build every [[bin]] inside rust:1.93.0-bullseye (glibc 2.31 floor) + run: | + set -euo pipefail + DOCKER=docker; docker ps >/dev/null 2>&1 || DOCKER="sudo -n docker" + T="${{ matrix.target }}" + CACHE="$(cd "$GITHUB_WORKSPACE/../.." && pwd)/cache-allbins/$T" + mkdir -p "$CACHE/registry" "$CACHE/target" "$GITHUB_WORKSPACE/target" + $DOCKER run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -v "$CACHE/registry:/usr/local/cargo/registry" \ + -w /workspace \ + rust:1.93.0-bullseye \ + cargo metadata --locked --no-deps --format-version 1 > "$RUNNER_TEMP/metadata.json" + ALL=$(python3 "$RUNNER_TEMP/nightly_manifest.py" bins --metadata "$RUNNER_TEMP/metadata.json") + BINS=$(echo "$ALL" | tr ',' '\n' | grep -vxE 'apr|pv' | paste -sd, -) + if [ -z "$BINS" ]; then echo "::error::no [[bin]] derived from the tag tree"; exit 1; fi + # the nightly's own cargo selection, verbatim (it builds apr and pv too; they are not packaged) + ARGS=$(python3 "$RUNNER_TEMP/nightly_manifest.py" bins --metadata "$RUNNER_TEMP/metadata.json" --format cargo) + echo "BINS=$BINS" >> "$GITHUB_ENV" + echo "Shipping $(echo "$BINS" | tr ',' '\n' | wc -l) bins (+ apr, pv from their own lanes): $BINS" + APR_SHA=$(git rev-parse --short=9 HEAD) + $DOCKER run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -v "$CACHE/registry:/usr/local/cargo/registry" \ + -v "$CACHE/target:/workspace/target" \ + -w /workspace \ + -e CARGO_TARGET_DIR=/workspace/target \ + -e CARGO_INCREMENTAL=0 \ + -e APR_GIT_SHA_OVERRIDE="$APR_SHA" \ + -e T="$T" \ + -e ARGS="$ARGS" \ + rust:1.93.0-bullseye \ + sh -c 'set -e; ldd --version | head -1; cargo build --locked --release $ARGS --target "$T"' + mkdir -p "target/$T/release" + # a bin that did not build fails this cp, which fails the step + for b in ${BINS//,/ }; do cp "$CACHE/target/$T/release/$b" "target/$T/release/$b"; done + + # Checks that can fail, on the box that built them (native arch): every bin answers --help, its + # --version belongs to the tag (stdout only: aprender-ptx-debug's deprecation notice is stderr), + # the nightly's verdicts hold, and no gnu asset imports above GLIBC_2.31. + - name: Every bin runs, belongs to the tag, and keeps the 2.31 floor + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -uo pipefail + T="${{ matrix.target }}"; SHA=$(git rev-parse HEAD); bad=0 + V=$(awk '/^\[workspace.package\]/{p=1;next} /^\[/{p=0} p&&/^version *=/{gsub(/"/,"",$3);print $3;exit}' Cargo.toml) + for b in ${BINS//,/ }; do + BIN="target/$T/release/$b" + line=$(timeout 30 "$BIN" --version 2>/dev/null | head -1) + if ! bash "$RUNNER_TEMP/asset_version_check.sh" "$TAG" "$SHA" "$line" "$b"; then bad=1; fi + if ! timeout 30 "$BIN" --help > /dev/null 2>&1; then echo "::error::$b --help failed"; bad=1; fi + FLOOR=$(objdump -T "$BIN" | grep -oE 'GLIBC_[0-9]+\.[0-9]+(\.[0-9]+)?' | sort -u -t_ -k2 -V | tail -1) + got=${FLOOR#GLIBC_} + if [ -n "$got" ] && [ "$(printf '%s\n%s\n' "$got" 2.31 | sort -V | tail -1)" != 2.31 ]; then + echo "::error::$b needs $FLOOR, above the GLIBC_2.31 floor"; bad=1 + fi + done + python3 "$RUNNER_TEMP/nightly_manifest.py" smoke --sha "$SHA" --bins "$BINS" \ + --bin-dir "target/$T/release" --version "$V" > "$RUNNER_TEMP/smoke.json" || bad=1 + { echo "### all [[bin]]s $T: $(echo "$BINS" | tr ',' '\n' | wc -l) bins at $TAG" + echo '```json'; cat "$RUNNER_TEMP/smoke.json"; echo '```'; } >> "$GITHUB_STEP_SUMMARY" + if [ "$bad" -ne 0 ]; then echo "::error::a [[bin]] does not belong to $TAG; nothing is uploaded"; exit 1; fi + + - name: Package archives + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + T="${{ matrix.target }}" + mkdir -p dist + for b in ${BINS//,/ }; do + A="$b-$TAG-$T" + mkdir -p "$A" + cp "target/$T/release/$b" "$A/" + for f in README.md LICENSE LICENSE-MIT LICENSE-APACHE; do + if [ -f "$f" ]; then cp "$f" "$A/"; fi + done + tar czf "dist/$A.tar.gz" "$A" + (cd dist && shasum -a 256 "$A.tar.gz" > "$A.tar.gz.sha256") + done + find dist -type f | wc -l + + # The pv lane's REST upload (these boxes carry no `gh`), then a read-back: every packaged file + # must be on the release, or the lane is red. + - name: Upload assets to release (REST, no gh) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + API="https://api.github.com/repos/${GITHUB_REPOSITORY}" + rel_json() { + curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG" + } + rel=$(rel_json) + rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') + cd dist + for f in *; do + aid=$(printf '%s' "$rel" | python3 -c 'import json,sys; n=sys.argv[1]; print(next((a["id"] for a in json.load(sys.stdin)["assets"] if a["name"]==n), ""))' "$f") + if [ -n "$aid" ]; then curl -sSf -X DELETE -H "Authorization: Bearer $GH_TOKEN" "$API/releases/assets/$aid" > /dev/null; fi + curl -sSf -X POST -H "Authorization: Bearer $GH_TOKEN" -H "Content-Type: application/octet-stream" \ + --data-binary @"$f" "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/$rid/assets?name=$f" \ + | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' + done + have=$(rel_json | python3 -c 'import json,sys; print("\n".join(a["name"] for a in json.load(sys.stdin)["assets"]))') + missing=0 + for f in *; do + if ! grep -qxF -- "$f" <<< "$have"; then echo "::error::$f is not on $TAG after upload"; missing=1; fi + done + [ "$missing" -eq 0 ] + echo "- $(find . -type f | wc -l) assets on $TAG, read back" >> "$GITHUB_STEP_SUMMARY" + build-apr-cuda: name: apr (cuda) ${{ matrix.target }} on ${{ matrix.host }} needs: assets @@ -918,7 +1094,7 @@ jobs: summary: name: Summary - needs: [assets, build, build-apr-cuda, build-apr-cpu, build-apr-darwin, verify-apr-assets, smoke-cuda, smoke-cpu] + needs: [assets, build, build-all-bins, build-apr-cuda, build-apr-cpu, build-apr-darwin, verify-apr-assets, smoke-cuda, smoke-cpu] runs-on: [self-hosted, Linux, X64, clean-room] if: always() steps: @@ -943,6 +1119,7 @@ jobs: else echo "- Build matrix: **partial** (status=$STATUS)" fi + echo "- every other [[bin]] (G5 #4189, x86_64+aarch64 gnu): ${{ needs.build-all-bins.result }}" echo "- Targets:" echo " - x86_64-unknown-linux-musl" echo " - x86_64-unknown-linux-gnu" diff --git a/crates/aprender-cgp/src/cli.rs b/crates/aprender-cgp/src/cli.rs index 3af89083bc..b8e12e72bf 100644 --- a/crates/aprender-cgp/src/cli.rs +++ b/crates/aprender-cgp/src/cli.rs @@ -24,7 +24,7 @@ pub const CGP_BACKEND_VALUES: [&str; 7] = /// Profiles scalar, SIMD (SSE2/AVX2/AVX-512/NEON/WASM SIMD128), /// wgpu (Vulkan/Metal/DX12/WebGPU), and CUDA workloads. #[derive(Parser, Clone, Debug)] -#[command(name = "cgp", version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"), about, long_about = None)] +#[command(name = "aprender-cgp", version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"), about, long_about = None)] pub struct Cli { /// Output JSON instead of human-readable text #[arg(long, global = true)] diff --git a/docs/audits/impl-GH-4189-receipt.md b/docs/audits/impl-GH-4189-receipt.md new file mode 100644 index 0000000000..c7d212c30f --- /dev/null +++ b/docs/audits/impl-GH-4189-receipt.md @@ -0,0 +1,35 @@ +# GH-4189 receipt: G5, the RELEASE half (PR #4580) + +## Scope: which half of #4189 this PR is +#4189 asks for two things: (a) the nightly builds every [[bin]] at main head and publishes a SHA +manifest for lambda, and (b) the 0.70 release gate G5, where every [[bin]] ships on the tag. + +- **(a) is already on the base branch, and this PR does not touch it.** `.github/workflows/nightly.yml` + derives every bin with `scripts/nightly_manifest.py bins` (l.129-130), builds them at the pushed + sha, packages `-.tar.gz` + `.sha256`, and publishes `nightly-manifest.json` (per-bin + sha256, executable sha256, --version, build sha) on the `nightly` prerelease (l.24, l.63). That + manifest is the lambda SHA manifest. +- **(b) is this PR**, per the cop's G5 brief: "Edit binary-release.yml to publish all 29 [[bin]]s, + reusing nightly.yml's bin matrix and manifest rather than a second list. Include the + asset_version_check.sh version-format fix." + +## What the diff does +1. `build-all-bins` in binary-release.yml. + - The bin set and the cargo selection are `nightly_manifest.py bins` of the tag tree (the nightly's + own derivation). + - Every bin must pass, before upload: `asset_version_check.sh` under its own name, `--help`, the + GLIBC_2.31 floor, and `nightly_manifest.py smoke`. smoke is the nightly manifest's per-bin + verdicts, written to the job summary. + - After upload, every asset is read back from the release. + - apr and pv keep their dedicated lanes. +2. `asset_version_check.sh` takes an optional BIN (default apr) and accepts pv/pv-sat's trailing + ` ()`. +3. `aprender-cgp` prints its [[bin]] name. Its clap name was `cgp`. + +## Measured +- A stamped v0.70.0-rc.1 build at 9f5609568 (29 bins), with each bin's `--version` stdout checked + under its own name: 29/29 ok. aprender-cgp was re-checked after the rename: + `aprender-cgp 0.70.0 (9f5609568f)`. +- `asset_version_check.sh --self-test` PASS, with 12 new rows. Two mutants turn it red: ignoring the + bin name gives 6 FAIL rows, and a greedy sha field gives 1. +- `cargo test -p aprender-cgp --lib --test integration`: 121 + 29 passed. `cargo fmt --check`: clean. diff --git a/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/findings.sarif b/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/findings.sarif new file mode 100644 index 0000000000..acb6a5c2e3 --- /dev/null +++ b/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/findings.sarif @@ -0,0 +1,215 @@ +{ + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "name": "pmat", + "informationUri": "https://github.com/paiml/pmat", + "version": "3.41.1", + "rules": [] + } + }, + "invocations": [ + { + "executionSuccessful": true, + "commandLine": "pmat query \"asset version check bin name\" --limit 5 --format json" + } + ], + "results": [ + { + "ruleId": "PRREV-DUP-001", + "level": "note", + "message": { + "text": "No prior implementation of a multi-bin asset_version_check found. pmat's semantic index (94,064 functions, Rust-only reach) returned nothing that duplicates the generalised avc_decide() bin-name-prefix logic this PR adds; top hits were unrelated build.rs / release-schedule doc chunks. This is a lexical-reach gap, not a clean bill: the index cannot see other shell scripts by symbol, only by name, so a second hand-rolled per-bin version checker under a different filename would not surface here." + }, + "properties": { + "grounding": "measured", + "command": [ + "pmat", + "query", + "asset version check bin name", + "--limit", + "5", + "--format", + "json" + ], + "exit_code": 0, + "stdout_sha256": "see-scratch-log", + "failure_scenario": "Accepting the pmat hit list as proof of no duplication would miss a second per-bin version-string parser written as a fresh shell script rather than a Rust function, since pmat's semantic index is Rust-only.", + "precision_class": "advisory" + } + }, + { + "ruleId": "PRREV-CORRECT-001", + "level": "note", + "message": { + "text": "CORRECTED after coordinator re-check: `python3 scripts/nightly_manifest.py bins --metadata ` (the function build-all-bins actually calls) prints 29 bin names and `ttop` is NOT among them. crates/aprender-viz-ttop/Cargo.toml sets `publish = false` (\"Internal system-monitor binary; ships via apr binary, not as a library dep\"), and workspace_bins() excludes non-published crates from the derived bin set -- so build-all-bins never builds or version-checks ttop today; my original 'error' framing (the job goes red on ttop) was wrong, verified by actually running the manifest command rather than reasoning from Cargo.toml's [[bin]] table alone. Retained as an ADVISORY note only: crates/aprender-viz-ttop/src/main.rs:27 still declares `#[command(name = \"ttop\", version, about, long_about = None)]` with clap's bare `version` (no `()` suffix), and if `publish = false` were ever dropped, or ttop moved into the derived bin set some other way, it would fail asset_version_check.sh's version-line regex immediately -- the same class of bug this PR's own aprender-cgp fix addresses, just not live in this diff's actual gate surface." + }, + "properties": { + "grounding": "measured", + "command": [ + "python3", + "scripts/nightly_manifest.py", + "bins", + "--metadata", + "" + ], + "exit_code": 0, + "stdout_sha256": "see-scratch-log", + "failure_scenario": "Only reachable if crates/aprender-viz-ttop/Cargo.toml's `publish = false` is later removed, or ttop is added to the derived bin set by some other path -- not reachable through this PR's diff as merged today.", + "precision_class": "advisory" + } + } + ] + }, + { + "tool": { + "driver": { + "name": "nvidia-cuda-docs", + "informationUri": "https://developer.nvidia.com/cuda-toolkit" + } + }, + "invocations": [ + { + "executionSuccessful": false, + "toolExecutionNotifications": [ + { + "level": "error", + "message": { + "text": "The guard's own --match-path predicate, recomputed over the FULL base_sha..head_sha range (aca6f2d7f6..e97655a868, evidence/pr-review/ excluded, 1628 paths -- this branch is stacked on batch/ont-10, so this range is not this PR's own diff), fires on 55 paths including .github/workflows/cuda-nightly.yml, crates/aprender-gpu/**, crates/aprender-serve/src/cuda/**, crates/aprender-ptx-debug/**. None of the 55 are among the 4 files this PR's own commits (ae34ee7cdd, e97655a868) touch -- confirmed by diffing origin/batch/ont-10...HEAD, which returns exactly the 4 reviewed files. nvidia-cuda-docs was not queried: the two-mark rule (S3.0) requires recording the trigger honestly rather than reasoning that 'this PR itself has no CUDA content' and calling it not-triggered, since the guard recomputes the trigger over base..head, not over a hand-picked commit range." + }, + "properties": { + "grounding": "measured", + "command": [ + "bash", + "-c", + "git diff --name-only aca6f2d7f6c436426ab3d78721474853f82a8448 e97655a8686d17d09216ad82b7326548b110c897 -- . ':!evidence/pr-review/' | xargs -I{} scripts/check_pr_review_receipt.sh --match-path {}" + ], + "exit_code": 0 + } + } + ] + } + ], + "results": [ + { + "ruleId": "PRREV-CUDA-CARRIED-001", + "level": "note", + "message": { + "text": "All 55 CUDA-path-trigger matches in base..head are pre-existing batch/ont-10 / car content already in that branch's own history, not authored by this PR's two commits (ae34ee7cdd feat(release): G5, e97655a868 docs(audit): GH-4189 receipt). This PR's own diff (origin/batch/ont-10...HEAD) touches only .github/workflows/binary-release.yml, crates/aprender-cgp/src/cli.rs, docs/audits/impl-GH-4189-receipt.md and scripts/release/asset_version_check.sh -- none CUDA-path-matching. Recorded as unreachable/carried-forward rather than not-triggered, per S3.0(c): narrating around the guard's own recomputed trigger is what this rule forbids." + }, + "properties": { + "grounding": "measured", + "command": [ + "bash", + "-c", + "git diff --name-only origin/batch/ont-10...HEAD" + ], + "exit_code": 0, + "stdout_sha256": "see-scratch-log", + "failure_scenario": "Reporting cuda as cleanly not-triggered on this receipt would misstate what the guard actually recomputes (the full base..head range, not this PR's own 2-commit delta), and the guard's own positive-control rejects exactly that misstatement.", + "precision_class": "advisory" + } + } + ] + }, + { + "tool": { + "driver": { + "name": "crux", + "informationUri": "internal:crux" + } + }, + "invocations": [ + { + "executionSuccessful": true, + "commandLine": "git diff aca6f2d7f6..e97655a868 -- | grep -E '^\\+' | grep -E \"$CRUX_SURFACE_RE\" (re-run of the guard's own S3.C regex against only this PR's own changed lines)" + } + ], + "results": [ + { + "ruleId": "PRREV-CRUX-001", + "level": "note", + "message": { + "text": "Re-running the guard's own CRUX_SURFACE_RE against this PR's own changed lines (not the full base..head range) finds two matches. (1) crates/aprender-cgp/src/cli.rs: `#[command(name = \"aprender-cgp\", ...)]` -- a REAL surface change: the aprender-cgp binary's clap command name changes from \"cgp\" to \"aprender-cgp\", which changes the leading token of its `--help`/`--version` output. This is the exact surface asset_version_check.sh's new bin-name-prefix stripping (avc_decide's 4th arg) depends on -- the fix and the surface it changes are the same commit, and the new self-test rows for 'aprender-cgp' cover it, so the gap here is zero: the one contract this surface touches (bin-name-prefixed version-line parsing) is exercised by the PR's own 30-row self-test. (2) .github/workflows/binary-release.yml: 4 occurrences of `APR_SHA=$(git rev-parse --short=9 HEAD)` / `APR_GIT_SHA_OVERRIDE=... --short=9` match the guard's `short[[:space:]]*=` sub-pattern, but this is `git rev-parse`'s own `--short=9` flag inside a shell heredoc, not a clap `#[arg(short = ...)]` declaration -- a regex false positive, not a CLI surface." + }, + "properties": { + "grounding": "measured", + "command": [ + "bash", + "-c", + "git diff aca6f2d7f6c436426ab3d78721474853f82a8448 e97655a8686d17d09216ad82b7326548b110c897 -- .github/workflows/binary-release.yml crates/aprender-cgp/src/cli.rs scripts/release/asset_version_check.sh docs/audits/impl-GH-4189-receipt.md | grep -E '^\\+' | grep -E \"$CRUX_SURFACE_RE\"" + ], + "exit_code": 0, + "failure_scenario": "Recording crux as not-triggered (as this receipt originally did, checking only the full base..head range and finding it 'noisy' would hide the one real surface change this PR makes -- the clap command name -- and would also fail the guard's own S3.C positive control, which recomputes the trigger per-PR-diff-line, not just over the wide range.", + "precision_class": "advisory" + } + } + ] + }, + { + "tool": { + "driver": { + "name": "cargo-mutants", + "informationUri": "https://mutants.rs" + } + }, + "invocations": [ + { + "executionSuccessful": false, + "toolExecutionNotifications": [ + { + "level": "error", + "message": { + "text": "cargo mutants was not run this session: bounded review session on a shared host, no dedicated compute reserved. The diff's only Rust hunk is a one-line clap name string change in crates/aprender-cgp/src/cli.rs; scripts/release/asset_version_check.sh is a bash guard with its own committed case table, which WAS run directly (30/30 rows, self-test PASS, see the pmat run's stdout_sha256 note) -- that is a real falsification result, just not cargo-mutants." + }, + "properties": { + "grounding": "measured", + "command": [ + "bash", + "scripts/release/asset_version_check.sh", + "--self-test" + ], + "exit_code": 0 + } + } + ] + } + ], + "results": [] + }, + { + "tool": { + "driver": { + "name": "antigravity", + "informationUri": "internal:agy" + } + }, + "invocations": [ + { + "executionSuccessful": false, + "toolExecutionNotifications": [ + { + "level": "error", + "message": { + "text": "agy was not dispatched this session: this review lane's sandbox is git-read-only and process-restricted (subagent-lock hook refused a git-fetch and a git-patch-id invocation typed directly at the top level during this same review), and no disposable-tree agy run was attempted within the session's time/compute budget." + }, + "properties": { + "grounding": "measured", + "command": [ + "bash", + "-c", + "git fetch origin main --quiet" + ], + "exit_code": 1 + } + } + ] + } + ], + "results": [] + } + ] +} \ No newline at end of file diff --git a/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/receipt.intoto.jsonl b/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/receipt.intoto.jsonl new file mode 100644 index 0000000000..b7c9ff3acd --- /dev/null +++ b/evidence/pr-review/4580/e97655a8686d17d09216ad82b7326548b110c897/receipt.intoto.jsonl @@ -0,0 +1 @@ +{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"pr-4580","digest":{"sha1":"e97655a8686d17d09216ad82b7326548b110c897"}}],"predicateType":"https://paiml.dev/attestations/pr-review/v2","predicate":{"skill_version":"2.1.0","attestation_level":"L1-self","pr":4580,"base_sha":"aca6f2d7f6c436426ab3d78721474853f82a8448","head_sha":"e97655a8686d17d09216ad82b7326548b110c897","reviewed_commits":["ae34ee7cdd","e97655a868"],"commits_outside_this_pr_note":"base..head also contains batch/ont-10 content merged/rebased under this branch before these two commits; base_sha=aca6f2d7f (merge-base with origin/main) predates the PR's own commits, and this range is the same one #4502's receipts recorded as reviewing only their own delta on top of the shared base. This receipt reviews ONLY ae34ee7cdd (G5 build-all-bins feat) and e97655a868 (docs-only audit receipt), the two commits unique to 1c/g5-release-all-bins; everything else reachable in base..head is prior batch/ont-10 / car history already reviewed on its own branch and out of scope here.","author_actor":{"kind":"agent","id":"agent:claude-opus-5-5/pr-4580-g5-author"},"reviewer_actor":{"kind":"agent","id":"agent:claude-sonnet-5/pr-4580-review"},"affected_crates":["aprender-cgp"],"verdict":"DEGRADED","degraded_reason":"cuda.status=unreachable: the guard's own --match-path predicate, recomputed over the FULL base_sha..head_sha range (aca6f2d7f6..e97655a868 -- this branch is stacked on batch/ont-10, so base..head is not this PR's own diff), fires on 55 pre-existing CUDA-path files carried in by that shared history (cuda-nightly.yml, crates/aprender-gpu/**, crates/aprender-serve/src/cuda/**, crates/aprender-ptx-debug/**); none of the 55 are among the 4 files this PR's own two commits (ae34ee7cdd, e97655a868) touch, and nvidia-cuda-docs was not queried this session. crux.status=consulted: the guard's CRUX_SURFACE_RE re-run against only this PR's own changed lines DOES fire (crates/aprender-cgp/src/cli.rs clap command-name change); reviewed directly, gap effect is none since the PR's own self-test covers that exact surface -- this does not degrade the verdict. mutation.status=unreachable: cargo mutants was not run this session (bounded review, shared host); the diff's Rust hunk is a 1-line clap name-string change, and the shell guard it also touches (asset_version_check.sh) was falsified directly via its own committed --self-test case table instead (30/30 rows PASS), which is real but is not cargo-mutants coverage of crates/aprender-cgp/src/cli.rs. antigravity.status=unreachable: no disposable-tree agy dispatch was attempted this session -- this review lane's sandbox refused a git fetch and a directly-typed git-patch-id call as non-read-only, and no agy run was attempted within the session's budget as a result. Per S3.0/S6, any unreachable consultation forces DEGRADED regardless of finding severity; the review otherwise found one real, measured, zero-gap surface change (PRREV-CRUX-001) and one advisory note (PRREV-CORRECT-001, corrected after re-check: ttop is not in build-all-bins' derived bin set today, publish=false excludes it).","consultations":{"pmat":{"status":"consulted","transport":"cli","transport_unavailable":["mcp: not attempted"],"index_commit":"e97655a8686d17d09216ad82b7326548b110c897","index_is_ancestor":true,"index_worktree_dirty":true,"index_worktree_dirty_note":"untracked evidence/pr-review/4580/** being written by this review; not part of the reviewed diff","complexity_delta":[],"tdg_delta":[],"satd_introduced":[],"duplication_hits":[],"duplication_coverage":{"rust":"semantic","shell":"lexical","python":"none","config":"none","docs":"none","other":"none","sibling_branches":"none","merge_base_to_main":"none"},"duplication_coverage_note":"Only pmat query (Rust-semantic reach) was run, plus the shell guard's own self-test (a falsification result, not a duplication scan). scripts/pr_review_duplication_scan.sh (the sibling/merge-base-to-main horizon sweep) was NOT run this session -- record honestly as 'none' on those surfaces rather than implying they were swept.","cache_hits":0,"queries_run":["asset version check bin name"],"findings":["PRREV-DUP-001 (note): no duplicate multi-bin version-check implementation found in pmat's Rust-only semantic index (lexical-reach caveat noted)","PRREV-CORRECT-001 (note, advisory, CORRECTED): ttop is NOT in build-all-bins' derived bin set (scripts/nightly_manifest.py bins prints 29 bins, ttop absent -- aprender-viz-ttop has publish=false, excluded by workspace_bins()); original 'error' framing was wrong. Retained as advisory: ttop's clap version string still lacks a () suffix and would fail the same gate if publish=false were ever dropped."],"duplication_horizon":["head=e97655a8686d17d09216ad82b7326548b110c897 (pmat semantic query run, Rust-only reach)","siblings=refs/remotes/* (3298 remote branches; not scanned this session -- scripts/pr_review_duplication_scan.sh was not run)","merge_base_to_main=aca6f2d7f6c436426ab3d78721474853f82a8448..origin/main (not scanned this session)"],"horizon_branches_total":3298,"horizon_branches_scanned":0,"symbols_searched":1},"cuda":{"status":"unreachable","trigger_reason":"guard's --match-path predicate, recomputed over the full base_sha..head_sha range (this branch is stacked on batch/ont-10), fires on 55 pre-existing CUDA-path files carried in by that shared history; none are among the 4 files this PR's own 2 commits touch","delta_path_match":true,"delta_path_match_count":55,"this_prs_own_commits_match_count":0,"probe":{"grounding":"measured","command":["bash","-c","git diff --name-only aca6f2d7f6c436426ab3d78721474853f82a8448 e97655a8686d17d09216ad82b7326548b110c897 -- . ':!evidence/pr-review/' | xargs -I{} scripts/check_pr_review_receipt.sh --match-path {}"],"exit_code":0,"result":"55 of 1628 base..head paths match; 0 of this PR's own 4 changed files match; nvidia-cuda-docs was not queried"}},"crux":{"status":"consulted","trigger_reason":"guard's CRUX_SURFACE_RE, re-run against only this PR's own changed lines (not the full base..head range), matches crates/aprender-cgp/src/cli.rs's #[command(name=...)] line","surfaces":["crates/aprender-cgp/src/cli.rs: clap command name \"cgp\" -> \"aprender-cgp\" (changes --help/--version leading token)"],"contracts":["scripts/release/asset_version_check.sh bin-name-prefix parsing (avc_decide 4th arg) -- this PR's own self-test covers the aprender-cgp case"],"gap_effect":"none","crux_coverage":"covered","false_positive_note":"4 occurrences of 'git rev-parse --short=9 HEAD' in .github/workflows/binary-release.yml also match the guard's regex (short[[:space:]]*=) but are a shell flag, not a clap arg declaration","comparative_claims":[]},"mutation":{"status":"unreachable","scope":"guard-adjacent","reason":"cargo mutants not run this session (bounded review, shared host, no reserved compute). scripts/release/asset_version_check.sh's own committed self-test (case table, 30 rows) WAS run and is recorded as measured; that covers the bash guard's falsification requirement but not cargo-mutants coverage of the crates/aprender-cgp/src/cli.rs one-line Rust change.","attempted":0,"self_test_run":{"command":["bash","scripts/release/asset_version_check.sh","--self-test"],"exit_code":0,"rows":30,"result":"PASS"}},"antigravity":{"status":"unreachable","attempted":0,"reason":"no disposable-tree agy dispatch attempted this session; review sandbox refused a git fetch and a directly-invoked git-patch-id as non-read-only, so the arm was not exercised within this session's budget","divergence":{"agreed":0,"agy_only":0,"primary_only":0,"contradicted":0},"findings":[]}},"findings_ref":{"path":"findings.sarif","sha256":"7e20136dae85ebb50e45d8efde742ce489129e48a579f716a803ff12dddf88ee"},"diff_patch_id":"009b3eaa6d1d0b2a56fc3424cb1ad01f34a22a48","diff_patch_id_algo":"git-patch-id-verbatim/pinned-diff-v1 (scripts/lib/pr_review_patch_id.sh prpid_compute), over base=aca6f2d7f6..head=e97655a868 -- . :(exclude)evidence/pr-review/4580","cost":{"input_tokens":0,"output_tokens":0,"wall_seconds":0}}} diff --git a/scripts/release/asset_version_check.sh b/scripts/release/asset_version_check.sh index 79ffc9b5d8..740192122e 100755 --- a/scripts/release/asset_version_check.sh +++ b/scripts/release/asset_version_check.sh @@ -1,11 +1,14 @@ #!/usr/bin/env bash # asset_version_check.sh — does a release asset belong to its tag? (#4275, RC-DOGFOOD-001) # -# bash scripts/release/asset_version_check.sh TAG COMMIT "VERSION_LINE" +# bash scripts/release/asset_version_check.sh TAG COMMIT "VERSION_LINE" [BIN] # bash scripts/release/asset_version_check.sh --self-test # # TAG is vX.Y.Z or vX.Y.Z-rc.N. COMMIT is the full sha the tag points at. -# VERSION_LINE is the first line of the asset's `apr --version`: `apr X.Y.Z ()`. +# VERSION_LINE is the first line of the asset's ` --version`: ` X.Y.Z ()`. +# BIN defaults to apr. Every [[bin]] of the release (#4189 G5, `nightly_manifest.py bins`) +# prints the same ` X.Y.Z ()` shape, so one check serves all 29; the line must +# name the bin it was read from, so a pv line offered for apr (or the reverse) is refused. # # The printed version must equal the tag EXACTLY, -rc.N included (operator 2026-09-24: "we # need actual version numbers", "version number needs release canidate info in it"). The rc @@ -32,8 +35,11 @@ PROG=asset_version_check # Pure. Prints `ok at ` or `bad `. avc_decide() { - local tag=$1 commit=$2 line=$3 want got sha + local tag=$1 commit=$2 line=$3 bin=${4:-apr} want got sha rest local promoted="" + if [[ ! $bin =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]]; then + echo "bad bin name '$bin'"; return + fi if [[ $tag =~ ^v([0-9]+\.[0-9]+\.[0-9]+)(-rc\.[0-9]+)?$ ]]; then want=${tag#v} else @@ -42,10 +48,13 @@ avc_decide() { if [[ ! $commit =~ ^[0-9a-f]{40}$ ]]; then echo "bad commit '$commit' is not a full 40-hex sha"; return fi - if [[ $line =~ ^apr\ ([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?)\ \((.*)\)$ ]]; then + # the bin name is compared as a literal prefix, never spliced into the regex + rest=${line#"$bin "} + # the sha is the FIRST parenthesised field; pv and pv-sat append ` ()` after it + if [[ $rest != "$line" && $rest =~ ^([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?)\ \(([^\)]*)\)(\ \(.*\))?$ ]]; then got=${BASH_REMATCH[1]}; sha=${BASH_REMATCH[3]} else - echo "bad version line '$line' is not 'apr X.Y.Z[-rc.N] ()'"; return + echo "bad version line '$line' is not '$bin X.Y.Z[-rc.N] ()'"; return fi if [ "$got" != "$want" ]; then # a final tag carrying its own rc's bytes (#4286): X.Y.Z-rc.N on vX.Y.Z, nothing looser. @@ -66,11 +75,11 @@ avc_decide() { } self_test() { - local fail=0 got want tag line c=7ff50ec2a1f671ad031ba427a275b1f983031d66 + local fail=0 got want tag line bin c=7ff50ec2a1f671ad031ba427a275b1f983031d66 echo "$PROG self-test: case table" - # wanttagcommitversion linewhy - while IFS=$'\t' read -r want tag commit line why; do - got=$(avc_decide "$tag" "$commit" "$line") + # wanttagcommitversion linewhy[bin, default apr] + while IFS=$'\t' read -r want tag commit line why bin; do + got=$(avc_decide "$tag" "$commit" "$line" "${bin:-apr}") if [ "${got%% *}" = "$want" ]; then echo " ok $why"; else echo " FAIL $why: wanted $want, got '$got'"; fail=1; fi done <&2; return 2; fi + if [ "$#" -ne 3 ] && [ "$#" -ne 4 ]; then echo "$PROG: usage: TAG COMMIT \"VERSION_LINE\" [BIN] | --self-test" >&2; return 2; fi local verdict - verdict=$(avc_decide "$1" "$2" "$3") + verdict=$(avc_decide "$1" "$2" "$3" "${4:-apr}") echo "$PROG: $verdict" [ "${verdict%% *}" = ok ] }