From a2b6c9688486d87405d1f13f7e12ad8bc074901a Mon Sep 17 00:00:00 2001 From: kavyabhand Date: Sun, 2 Aug 2026 15:49:06 +0530 Subject: [PATCH 1/5] ci: select Docker Zig tarball by TARGETARCH Apple Silicon docker compose was installing x86_64 Zig into an arm64 Ubuntu image and failing at link with invalid ELF machine type AARCH64. --- .port-mortem.toml | 5 +++++ Dockerfile | 26 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.port-mortem.toml b/.port-mortem.toml index 03e21c0..12494f0 100644 --- a/.port-mortem.toml +++ b/.port-mortem.toml @@ -7,6 +7,11 @@ source_commit = "4a7456e2eab88b4c76053c1c4157639ccb930e2b" source_commit_date = "2025-12-12T13:47:05-06:00" target_language = "zig" target_zig_version = "0.16.0" +# Official ziglang.org SHA-256 values for 0.16.0 (Dockerfile selects by TARGETARCH). +target_zig_sha256_x86_64_linux = "70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00" +target_zig_sha256_aarch64_linux = "ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17" +target_zig_sha256_aarch64_macos = "b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489" +# Retained alias: x86_64-linux tarball (CI and historical docs). target_zig_sha256 = "70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00" license = "Zlib" kickoff_note = "Original test suite hashed at vendor commit; verify with scripts/verify-original-hashes.sh" diff --git a/Dockerfile b/Dockerfile index 2c2f568..d2ee8c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ # syntax=docker/dockerfile:1.7 # Reproducible verify image for tinyexpr.zig -# Zig tarball pinned by SHA-256. Base is ubuntu:24.04 (refresh digest at rebuild). +# Zig tarball pinned by SHA-256 per architecture. Base is ubuntu:24.04. +# TARGETARCH comes from BuildKit (amd64 / arm64). Without this, Apple Silicon +# Docker builds an arm64 Ubuntu image, downloads the x86_64 Zig binary, and +# fails at link time with "invalid ELF machine type: AARCH64". FROM ubuntu:24.04 AS base ENV DEBIAN_FRONTEND=noninteractive @@ -19,14 +22,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* \ && apt-get clean +ARG TARGETARCH ENV ZIG_VERSION=0.16.0 -ENV ZIG_SHA256=70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00 -RUN curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" \ - -o /tmp/zig.tar.xz \ - && echo "${ZIG_SHA256} /tmp/zig.tar.xz" | sha256sum -c - \ - && tar -xJf /tmp/zig.tar.xz -C /opt \ - && mv /opt/zig-x86_64-linux-${ZIG_VERSION} /opt/zig \ - && rm /tmp/zig.tar.xz +# Official ziglang.org SHA-256 for 0.16.0 (also recorded in .port-mortem.toml). +RUN set -eux; \ + case "${TARGETARCH}" in \ + amd64) ZIG_ARCH=x86_64; ZIG_SHA256=70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00 ;; \ + arm64) ZIG_ARCH=aarch64; ZIG_SHA256=ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17 ;; \ + *) echo "unsupported TARGETARCH=${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" \ + -o /tmp/zig.tar.xz; \ + echo "${ZIG_SHA256} /tmp/zig.tar.xz" | sha256sum -c -; \ + tar -xJf /tmp/zig.tar.xz -C /opt; \ + mv /opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION} /opt/zig; \ + rm /tmp/zig.tar.xz ENV PATH=/opt/zig:$PATH WORKDIR /src From 2f491afb32483366f2a411b885fad8b1abd32105 Mon Sep 17 00:00:00 2001 From: kavyabhand Date: Sun, 2 Aug 2026 15:49:06 +0530 Subject: [PATCH 2/5] chore: make verify scripts portable on macOS bash Empty-array set -u, BSD sed -i, sha256sum/shasum, and missing objcopy all broke the host cold-start path on Darwin. --- .gitignore | 4 ++++ scripts/run-enumerate.sh | 40 ++++++++++++++++++++++++------- scripts/run-original-suite.sh | 8 ++++--- scripts/verify-original-hashes.sh | 6 ++++- scripts/verify.sh | 17 +++++++++++-- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 7bbff8b..8a8b80f 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,7 @@ Thumbs.db # Port kcov working reports (fragment + coverage.md stay tracked) docs/evidence/coverage-port-kcov*/ docs/evidence/coverage-port.log + +# Layout probe host binaries +layout-c +layout-zig diff --git a/scripts/run-enumerate.sh b/scripts/run-enumerate.sh index 25751ad..83a8432 100644 --- a/scripts/run-enumerate.sh +++ b/scripts/run-enumerate.sh @@ -56,25 +56,49 @@ echo "ast_depth=$AST_DEPTH" cc -std=c11 -O3 -Wall -Wextra -Wno-array-bounds \ -c "$ROOT/third_party/tinyexpr-c/tinyexpr.c" -o "$ORIGINAL_OBJECT" -mapfile -t EXPORTED < <( - nm -g --defined-only "$ORIGINAL_OBJECT" | - awk '{print $3}' | - awk '/^te_|^next_token$/' +# Prefer GNU objcopy. macOS ships neither objcopy nor mapfile (bash 3.2). +OBJCOPY="" +for candidate in objcopy gobjcopy llvm-objcopy; do + if command -v "$candidate" >/dev/null 2>&1; then + OBJCOPY="$candidate" + break + fi +done +if [[ -z "$OBJCOPY" ]]; then + echo "SKIP enumeration: no objcopy on PATH (install binutils, or use docker compose up --build verify)" >&2 + echo "enumeration_skipped=missing_objcopy" + exit 0 +fi + +EXPORTED=() +while IFS= read -r symbol; do + [[ -n "$symbol" ]] || continue + EXPORTED+=("$symbol") +done < <( + # GNU nm accepts --defined-only; BSD/Apple nm does not. Fall back to + # filtering the symbol type column when the GNU flag is rejected. + if nm -g --defined-only "$ORIGINAL_OBJECT" >/dev/null 2>&1; then + nm -g --defined-only "$ORIGINAL_OBJECT" | awk '{print $3}' | awk '/^te_|^next_token$/' + else + nm -g "$ORIGINAL_OBJECT" | awk '$2 ~ /^[TDB]$/ {print $3}' | awk '/^_?(te_|next_token$)/ { sub(/^_/, ""); print }' + fi ) + RENAME_ARGS=() -for symbol in "${EXPORTED[@]}"; do +for symbol in ${EXPORTED[@]+"${EXPORTED[@]}"}; do RENAME_ARGS+=(--redefine-sym "$symbol=c_$symbol") done for required in te_compile te_eval te_free te_interp te_print te_free_parameters next_token; do - if [[ " ${EXPORTED[*]} " != *" ${required} "* ]]; then + if [[ " ${EXPORTED[*]-} " != *" ${required} "* ]]; then [[ "$required" == "next_token" ]] || { echo "missing exported C symbol: $required" >&2 exit 2 } fi done -objcopy "${RENAME_ARGS[@]}" "$ORIGINAL_OBJECT" "$RENAMED_OBJECT" -echo "renamed_symbols=${EXPORTED[*]}" +"$OBJCOPY" ${RENAME_ARGS[@]+"${RENAME_ARGS[@]}"} "$ORIGINAL_OBJECT" "$RENAMED_OBJECT" +echo "renamed_symbols=${EXPORTED[*]-}" +echo "objcopy=$OBJCOPY" (cd "$ROOT" && zig build -Doptimize=ReleaseFast -p "$PREFIX" --cache-dir "$CACHE") cc -std=c11 -O3 -Wall -Wextra -Werror \ diff --git a/scripts/run-original-suite.sh b/scripts/run-original-suite.sh index 4159d5e..f44b329 100644 --- a/scripts/run-original-suite.sh +++ b/scripts/run-original-suite.sh @@ -15,13 +15,15 @@ mkdir -p "$OUT" "$CACHE" NAME="${1:?config name}" shift -ZIG_FLAGS=("$@") +# Bash 3.2 (macOS /bin/bash) rejects "${arr[@]}" under `set -u` when empty. +# After shifting the config name off, remaining "$@" are Zig build flags. +# Expand with ${@+"$@"} so an empty flag list is safe under `set -u`. # Build in-tree (CI / Docker); avoid rsync dependency. cd "$ROOT" PREFIX="$ROOT/zig-out-$NAME" rm -rf "$PREFIX" -zig build "${ZIG_FLAGS[@]}" -p "$PREFIX" --cache-dir "$CACHE" +zig build ${@+"$@"} -p "$PREFIX" --cache-dir "$CACHE" LIBA="$PREFIX/lib/libtinyexpr.a" if [[ ! -f "$LIBA" ]]; then @@ -35,7 +37,7 @@ INCLUDE="$ROOT/third_party/tinyexpr-c" SMOKE="$ROOT/tests/original/smoke.c" CFLAGS=(-Wall -Wshadow -O2 -I"$INCLUDE") -for f in "${ZIG_FLAGS[@]}"; do +for f in ${@+"$@"}; do case "$f" in -Dpow-from-right=true) CFLAGS+=(-DTE_POW_FROM_RIGHT) ;; -Dnat-log=true) CFLAGS+=(-DTE_NAT_LOG) ;; diff --git a/scripts/verify-original-hashes.sh b/scripts/verify-original-hashes.sh index 7179747..ff55f37 100755 --- a/scripts/verify-original-hashes.sh +++ b/scripts/verify-original-hashes.sh @@ -20,7 +20,11 @@ while IFS= read -r line; do fail=1 continue fi - actual="$(sha256sum "$path" | awk '{print $1}')" + if command -v sha256sum >/dev/null 2>&1; then + actual="$(sha256sum "$path" | awk '{print $1}')" + else + actual="$(shasum -a 256 "$path" | awk '{print $1}')" + fi if [[ "$actual" != "$expect" ]]; then echo "MISMATCH $path" >&2 echo " expected $expect" >&2 diff --git a/scripts/verify.sh b/scripts/verify.sh index ca4ae24..b53d59f 100644 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -21,7 +21,12 @@ echo "== zig build test ==" zig build test --cache-dir "$CACHE" echo "== original suite (4 configs) ==" -sed -i 's/\r$//' "$ROOT/scripts/run-original-suite.sh" || true +# Portable CRLF strip: GNU sed accepts `sed -i`, BSD sed needs `sed -i ''`. +if sed --version >/dev/null 2>&1; then + sed -i 's/\r$//' "$ROOT/scripts/run-original-suite.sh" || true +else + sed -i '' 's/\r$//' "$ROOT/scripts/run-original-suite.sh" || true +fi bash "$ROOT/scripts/run-original-suite.sh" default bash "$ROOT/scripts/run-original-suite.sh" pow -Dpow-from-right=true bash "$ROOT/scripts/run-original-suite.sh" natlog -Dnat-log=true @@ -34,13 +39,21 @@ echo "== escape hatches ==" python3 "$ROOT/tools/count-escape-hatches.py" "$ROOT" echo "== short differential enumeration ==" -bash "$ROOT/scripts/run-enumerate.sh" --quick +if command -v objcopy >/dev/null 2>&1 || command -v gobjcopy >/dev/null 2>&1 || command -v llvm-objcopy >/dev/null 2>&1; then + bash "$ROOT/scripts/run-enumerate.sh" --quick +else + echo "SKIP short enumeration: objcopy not on PATH (Docker image includes binutils; host Mac needs brew binutils or docker compose)" +fi echo "== scorecard ==" python3 "$ROOT/tools/scorecard.py" "$ROOT" +echo "== render README scorecard region ==" +python3 "$ROOT/tools/render_readme.py" "$ROOT" + echo "== README claims ==" bash "$ROOT/scripts/check-readme-claims.sh" +bash "$ROOT/scripts/check-readme-rendered.sh" echo echo "VERIFY OK" From 3a55fd1b37e3792ceaeeb33b41726844ec5f9577 Mon Sep 17 00:00:00 2001 From: kavyabhand Date: Sun, 2 Aug 2026 15:49:06 +0530 Subject: [PATCH 3/5] ci: stop silently rewriting README before claim check The claims script re-rendered the scorecard region first, so a planted wrong fraction still exited 0. Check-only; verify renders explicitly. --- scripts/check-readme-claims.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/check-readme-claims.sh b/scripts/check-readme-claims.sh index 9cf471a..ce02087 100644 --- a/scripts/check-readme-claims.sh +++ b/scripts/check-readme-claims.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash # Fail if README.md numeric claims disagree with docs/evidence/scorecard.json. -# Regenerates the README scorecard region from the current scorecard first so -# verify pipelines that refresh escape-hatches/scorecard stay consistent. +# +# Intentionally does NOT rewrite README.md. A previous version called +# render_readme.py unconditionally, which silently corrected a wrong +# committed README and made the claim gate exit 0. Staleness is enforced +# by scripts/check-readme-rendered.sh (render_readme.py --check). Verify +# pipelines that regenerate the scorecard must render first, then call this. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" SC="$ROOT/docs/evidence/scorecard.json" @@ -13,6 +17,5 @@ if [[ ! -f "$SC" ]]; then exit 1 fi -python3 "$ROOT/tools/render_readme.py" "$ROOT" python3 "$CHECKER" "$SC" "$README" python3 "$CHECKER" --self-test From 04e1b0b5cf16246d8c6421d13d4ba25c74c0583b Mon Sep 17 00:00:00 2001 From: kavyabhand Date: Sun, 2 Aug 2026 15:49:06 +0530 Subject: [PATCH 4/5] docs: independent Mac reproduction and gate failure proofs Record cold-start friction, host-dependent nest cliffs, licence review, and which CI gates were watched failing on a second machine. --- .github/PULL_REQUEST_TEMPLATE.md | 40 ++++++++++++++ DECISIONS.md | 46 ++++++++++++++++ README.md | 14 ++++- docs/REPRODUCTION.md | 80 ++++++++++++++++++++++++++++ docs/evidence/gate-failure-proofs.md | 19 +++++++ docs/evidence/upstream-diff.txt | 5 ++ docs/journal.md | 36 +++++++++++++ 7 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 docs/REPRODUCTION.md create mode 100644 docs/evidence/gate-failure-proofs.md create mode 100644 docs/evidence/upstream-diff.txt diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9519b77 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ +## Summary + +- + +## Four assertion counts (required before merge) + +Run all four and paste the `ALL TESTS PASSED` lines: + +```bash +bash scripts/run-original-suite.sh default +bash scripts/run-original-suite.sh pow -Dpow-from-right=true +bash scripts/run-original-suite.sh natlog -Dnat-log=true +bash scripts/run-original-suite.sh pow_natlog -Dpow-from-right=true -Dnat-log=true +``` + +| Config | Required | Measured (Zig-linked) | Measured (C-linked) | +| --- | --- | --- | --- | +| default | 4930/4930 | | | +| pow | 4924/4924 | | | +| natlog | 4934/4934 | | | +| pow_natlog | 4928/4928 | | | + +## Evidence artifacts touched + +List paths under `docs/evidence/` (and `docs/evidence/fragments/`) this PR creates or regenerates: + +- + +## Gates exercised + +- [ ] `scripts/verify-original-hashes.sh` +- [ ] `scripts/fetch-upstream-and-diff.sh` (if network available) +- [ ] `zig build fmt-check` / `zig fmt --check` +- [ ] escape-hatch count (`tools/count-escape-hatches.py`) +- [ ] README claims / rendered scorecard region +- [ ] `zig build verify` and/or `docker compose up --build verify` + +## Notes for reviewers + +- diff --git a/DECISIONS.md b/DECISIONS.md index 1a9342d..1ac5249 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -374,3 +374,49 @@ Entries that only restate a diff will be deleted. Equivalence for decimal literals is scoped to the C locale. - Evidence: `docs/findings.md` Lead C, `docs/evidence/fragments/buglead-locale.json`. + +## D030 - Docker Zig tarball must follow TARGETARCH + +- Constraint: the brief's one-command path is `docker compose up`. Judges on + Apple Silicon get a linux/arm64 engine by default. The image previously + always downloaded `zig-x86_64-linux-0.16.0.tar.xz`. +- Options: force `platform: linux/amd64` in compose; document a manual flag; + select the Zig tarball from BuildKit `TARGETARCH`. +- Choice: Dockerfile switches on `TARGETARCH` (`amd64` -> x86_64 SHA + `70e49664...`, `arm64` -> aarch64 SHA `ea4b09bf...`). Both digests verified + against ziglang.org downloads and recorded in `.port-mortem.toml`. +- Consequence: cold `docker compose up --build verify` works on arm64 Docker + without qemu-user for the Zig toolchain itself. CI on ubuntu-24.04 amd64 is + unchanged. +- Evidence: first independent run failed with `invalid ELF machine type: + AARCH64`; after the change, VERIFY OK in 122.71s on M1 Docker + (`docs/REPRODUCTION.md`). + +## D031 - README claim gate must not rewrite before checking + +- Constraint: anti-overclaiming requires a checker that can fail. +- Options: keep auto-render-then-check; split render and check; check only. +- Choice: `scripts/check-readme-claims.sh` only compares. `scripts/verify.sh` + renders explicitly after regenerating the scorecard, then runs claims + + `check-readme-rendered.sh`. CI `readme-scorecard` already used `--check` + first; the claims script no longer undoes a planted overclaim. +- Consequence: a committed wrong fraction fails locally and in CI instead of + being silently corrected during the "check". +- Evidence: planted `4999/4999` exited 0 before the fix and 1 after + (`docs/evidence/gate-failure-proofs.md`). + +## D032 - Host script portability for cold clones + +- Constraint: second-machine / judge hosts are not the original WSL box. + macOS `/bin/bash` is old enough to reject empty-array expansion under + `set -u`; BSD `sed -i` needs an extension arg; `mapfile` and GNU `objcopy` + are absent without Homebrew binutils. +- Options: require Linux only; paper over in docs; fix the scripts. +- Choice: empty-array-safe flag expansion in `run-original-suite.sh`; portable + `sed -i` detection in `verify.sh`; `sha256sum`/`shasum` fallback; enumeration + skips cleanly when `objcopy` is missing, with Docker remaining the full path. +- Consequence: host `zig build test` and the four original-suite configs run on + Darwin; full verify still prefers Docker for enumeration and tool parity. +- Evidence: pre-fix empty-array unbound variable and `mapfile: command not + found` on this Mac; suites then measured at 4930/4924/4934/4928 + (`docs/REPRODUCTION.md`). diff --git a/README.md b/README.md index c58dcc7..8ba36fe 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,19 @@ four original-suite configs, layout probe, escape-hatch count, and writes ## Build -Requires Zig 0.16.0 (SHA-256 in `.port-mortem.toml` and `Dockerfile`). +Requires Zig 0.16.0. Official download SHA-256 values are pinned in +`.port-mortem.toml` and the `Dockerfile` (x86_64-linux, aarch64-linux, and +aarch64-macos). Verify the tarball checksum before unpacking. + +Prefer the Docker path on any host (including Apple Silicon). The image +selects the matching Linux Zig tarball via `TARGETARCH`, so +`docker compose up --build verify` is the one-command judge path. + +Host builds also need a C toolchain (`cc`/`gcc`), Python 3, and (for the +short enumeration tier inside `zig build verify`) GNU `objcopy` from +binutils. macOS ships an old `/bin/bash` that rejects empty-array expansion +under `set -u`; the scripts tolerate that. Without `objcopy`, the +enumeration step is skipped with an explicit message. ```bash zig build diff --git a/docs/REPRODUCTION.md b/docs/REPRODUCTION.md new file mode 100644 index 0000000..13ca7a3 --- /dev/null +++ b/docs/REPRODUCTION.md @@ -0,0 +1,80 @@ +# Independent reproduction + +Second-machine verification of the submission claims. Regenerated on a clean +clone; numbers below are measured here, not copied from prior evidence files. + +## Host + +| Field | Value | +| --- | --- | +| Hostname | Kavyas-MacBook-Air-2.local | +| OS | macOS 15.1.1 (Build 24B91) | +| Kernel | Darwin 24.1.0 (xnu-11215.41.3 ~2 RELEASE_ARM64_T8103) | +| CPU | Apple M1 (arm64), 8 cores | +| Shell | zsh; `/bin/bash` is GNU bash 3.2.57 | +| C compiler | Apple clang 16.0.0 (clang-1600.0.26.6) | +| Python | 3.14.6 | +| Docker | 29.2.0, server linux/arm64 | +| Zig (host) | 0.16.0 from `zig-aarch64-macos-0.16.0.tar.xz` | +| Zig tarball SHA-256 (host) | `b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489` (matched published) | +| Zig tarball SHA-256 (linux x86_64, pinned) | `70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00` (matched) | +| Zig tarball SHA-256 (linux aarch64, Docker) | `ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17` (matched download) | +| git SHA at measurement | `4116f8398b39983369cc6bd6e75783e020a67305` | +| UTC | 2026-08-02 | + +Bootstrap steps the docs did not fully spell out before this pass: + +1. Start Docker Desktop (daemon was not running after install; `docker compose` failed with a sock error until `open -a Docker`). +2. Install Zig 0.16.0 for the host arch (`aarch64-macos` here), not only the linux x86_64 hash named in older README text. +3. On the host, `scripts/run-original-suite.sh default` used to die under bash 3.2 (`ZIG_FLAGS[@]: unbound variable`) before any suite ran. +4. Host `zig build verify` needs GNU `objcopy` for the short enumeration tier; without it the step must skip or the verify fails on `mapfile`/`objcopy`. +5. Prefer Docker for the judge path so host variance does not matter. + +## Claim table + +| Claim | Repo asserted | Measured here | Host path | Reproduced? | +| --- | --- | --- | --- | --- | +| `docker compose up --build verify` one-command | works from clean clone | First run failed: Dockerfile always fetched `zig-x86_64-linux` into an arm64 Ubuntu image; link errors `invalid ELF machine type: AARCH64`. After multi-arch Dockerfile fix: **VERIFY OK**, wall time **122.71s** (`time -p`), suites 4930/4924/4934/4928, layout IDENTICAL, escape 16, `upstream_hash_ok` true | Docker linux/arm64 on M1 | **No, then Yes after fix** | +| `zig build verify` on host | works | Failed mid-run: `mapfile: command not found` in `run-enumerate.sh` (bash 3.2). After portable skip when `objcopy` missing: expected to complete (enumeration skipped with message) | Darwin host | **No, then fixed** | +| `scripts/verify-original-hashes.sh` | all matched | all matched | Darwin | Yes | +| `scripts/fetch-upstream-and-diff.sh` | smoke.c / minctest.h match pin `4a7456e2` | both OK | Darwin + network | Yes | +| Line endings / vendored hashes on foreign OS | LF preserved via `.gitattributes` | SHA-256 of all six hashed paths matched `.port-mortem.toml` after clone onto macOS | Darwin | Yes | +| Original suite default Zig-linked | 4930/4930 | 4930/4930 | Darwin + Zig 0.16.0 | Yes | +| Original suite pow Zig-linked | 4924/4924 | 4924/4924 | Darwin | Yes | +| Original suite natlog Zig-linked | 4934/4934 | 4934/4934 | Darwin | Yes | +| Original suite pow_natlog Zig-linked | 4928/4928 | 4928/4928 | Darwin | Yes | +| Same four counts C-linked | same fractions | same fractions | Darwin + Apple clang | Yes | +| Same four counts inside Docker verify | same | same (see docker log) | Docker arm64 Ubuntu 24.04 | Yes | +| Struct layout probe | IDENTICAL; te_expr 24/8, te_variable 32/8 | IDENTICAL; same sizes/offsets | Darwin | Yes | +| Escape hatches | 16 total, 0 outside `src/c_abi.zig`, threshold 20 | 16 total, 0 outside, pass true | Darwin | Yes | +| `zig build test` four flag combos | pass | pass | Darwin | Yes | +| `zig fmt --check` | clean | clean | Darwin | Yes | +| README claim checker | rejects wrong numbers | **Defect found**: `check-readme-claims.sh` re-rendered README before checking, so a planted `4999/4999` exited 0. After removing the silent rewrite: planted `4999/4999` exits 1; `check-readme-rendered.sh` also exits 1 | Darwin | **Gate fixed** | +| Escape-hatch gate | fails if hatch leaves `c_abi.zig` | Planted `@ptrCast` in `src/root.zig` -> exit 1, `ILLEGAL src/root.zig` | Darwin | Yes | +| Hash gate (tampered `tests/original/smoke.c`) | fail | exit 1, MISMATCH | Darwin | Yes | +| Hash gate (wrong toml digest) | fail | exit 1, MISMATCH | Darwin | Yes | +| `zig fmt --check` gate | fail on drift | doubled first newline in `src/ops.zig` -> exit 1 | Darwin | Yes | +| Claim-checker `--self-test` | rejects `tools/testdata/readme-wrong.md` | exit 0, "wrong README rejected" | Darwin | Yes | +| C `te_interp` nest crash threshold | first machine: OK 20143 / crash **20144** (ulimit -s 8192, Linux x86_64) | this machine: OK **23763** / crash **23764** (ulimit -s 8176, Darwin arm64) | Darwin | **Host-dependent; both real** | +| C `te_compile` nest crash threshold | first machine: OK 20146 / crash 20147 | this machine: OK **23764** / crash **23765** | Darwin | **Host-dependent; both real** | +| Cross-compile matrix statuses | aarch64 full_suite_passes; riscv64/wasm unit_tests_pass; others builds_only | Not re-emulated on this host (no qemu-user-static/wasmtime installed outside Docker). Artifact build of native `aarch64-macos` succeeds via `zig build`. Docker verify does not re-run the full cross matrix. | Darwin / Docker | Partial (native build only) | +| Licence / pin metadata | zlib obligations + `.port-mortem.toml` track/url/commit | Confirmed: `LICENSE` marks altered derivative; `THIRD_PARTY.md` names upstream/author/commit/zlib; `LICENSE-NOTICE.txt` retains upstream notice; `.port-mortem.toml` has track G, source URL, pin, hashes, Zig SHAs | read-only review | Yes | +| Upstream files unmodified | hashes match pin | `verify-original-hashes` + live fetch both OK | Darwin | Yes | + +## Friction fixed in this PR + +1. **Dockerfile multi-arch Zig** (`TARGETARCH` amd64/arm64 with pinned SHAs). Without this, Apple Silicon `docker compose up --build verify` is broken for judges. +2. **bash 3.2 empty-array safety** in `scripts/run-original-suite.sh`. +3. **Portable `sed -i`** in `scripts/verify.sh`. +4. **`sha256sum` fallback to `shasum`** in `scripts/verify-original-hashes.sh`. +5. **Enumeration**: replace `mapfile`; detect missing `objcopy`; skip with a clear message from `verify.sh` on hosts without binutils. +6. **README claim gate**: stop silently rewriting README before checking; verify path renders explicitly, then checks claims and freshness. +7. **README Build section**: cold-start prerequisites and Docker-first guidance. +8. **`.port-mortem.toml`**: record aarch64-linux and aarch64-macos Zig SHAs alongside the historical x86_64-linux pin. +9. **PR template**: prompts for the four assertion counts and evidence paths. + +## Reasoning notes + +- Nesting cliffs differ by thousands of frames between Linux x86_64 WSL and Darwin arm64 at nearly the same `ulimit -s`. That is stack-frame / ABI variance, not a port bug. Reporting both numbers is the honest claim. +- Suite assertion counts are configuration invariants, not host-dependent; they matched exactly on Darwin host and in arm64 Docker. +- Escape-hatch LOC / per-kloc can jitter slightly when the counter re-runs; the gated total (16) and outside count (0) are the invariants. diff --git a/docs/evidence/gate-failure-proofs.md b/docs/evidence/gate-failure-proofs.md new file mode 100644 index 0000000..15f6f70 --- /dev/null +++ b/docs/evidence/gate-failure-proofs.md @@ -0,0 +1,19 @@ +# CI gate failure proofs + +Measured 2026-08-02 on Kavyas-MacBook-Air-2.local (Darwin 24.1.0, Apple M1). +Each mutation was applied, the gate was run, the exit code recorded, then the +tree was restored. + +| Gate | Mutation | Exit | Observed | +| --- | --- | --- | --- | +| `scripts/check-readme-claims.sh` (before fix) | README `4930/4930` -> `4999/4999` | 0 (defect) | Script re-rendered README first, erasing the planted overclaim | +| `scripts/check-readme-claims.sh` (after fix) | same planted `4999/4999` | 1 | `numerator 4999 not backed in scorecard` | +| `scripts/check-readme-rendered.sh` | same planted `4999/4999` | 1 | `README scorecard region is stale` | +| `tools/count-escape-hatches.py` | append `@ptrCast` use in `src/root.zig` | 1 | `ILLEGAL src/root.zig` / `outside src/c_abi.zig: 1` | +| `scripts/verify-original-hashes.sh` | append byte to `tests/original/smoke.c` | 1 | `MISMATCH tests/original/smoke.c` | +| `scripts/verify-original-hashes.sh` | flip one hex digit in `.port-mortem.toml` hash | 1 | `MISMATCH tests/original/smoke.c` | +| `zig fmt --check src/ops.zig` | insert extra blank line at top of `src/ops.zig` | 1 | prints `src/ops.zig` | +| `tools/check_readme_claims.py --self-test` | none (fixture `tools/testdata/readme-wrong.md`) | 0 | `self-test OK: wrong README rejected` | + +A gate nobody has watched fail is not a gate. The README claim script was in +that state until this pass removed the silent rewrite. diff --git a/docs/evidence/upstream-diff.txt b/docs/evidence/upstream-diff.txt new file mode 100644 index 0000000..911b179 --- /dev/null +++ b/docs/evidence/upstream-diff.txt @@ -0,0 +1,5 @@ +fetching smoke.c @ 4a7456e2eab88b4c76053c1c4157639ccb930e2b +OK tests/original/smoke.c matches upstream +fetching minctest.h @ 4a7456e2eab88b4c76053c1c4157639ccb930e2b +OK tests/original/minctest.h matches upstream +fetch-upstream-and-diff: all matched diff --git a/docs/journal.md b/docs/journal.md index b45676f..5817a7a 100644 --- a/docs/journal.md +++ b/docs/journal.md @@ -229,3 +229,39 @@ true. Long differential fuzz (`scripts/run-fuzz.sh 14400`) is still running with progress dumps under `.local/fuzz/progress-long.json`; `fuzz/log-long.txt` will be committed when that campaign ends. Do not invent a total. +## 2026-08-02 Kavyas-MacBook-Air-2 independent-reproduction + +Second machine, clean clone on Apple M1 / macOS 15.1.1. Role was independent +reproduction, not more campaign volume. Do not touch the long fuzz territory. + +Cold start findings, in the order we hit them: + +1. Docker Desktop installed but daemon stopped. `docker compose` failed on the + sock until `open -a Docker`. +2. Host Zig must be the aarch64-macos 0.16.0 tarball. Its SHA-256 is + `b23d70de...`, distinct from the linux x86_64 pin. We verified both, plus the + aarch64-linux tarball SHA `ea4b09bf...` used once Docker is arm64-native. +3. First `docker compose up --build verify` built an arm64 Ubuntu image, then + ran the x86_64 Zig binary against aarch64 crt/libm and died with + `invalid ELF machine type: AARCH64`. That is exactly the "judge has only + Docker" failure mode. Fixed by selecting the Zig tarball from `TARGETARCH`. +4. After the Dockerfile fix, Docker verify printed VERIFY OK in 122.71 seconds + with the four suite counts intact. +5. Host `run-original-suite.sh default` crashed immediately on bash 3.2 empty + array expansion under `set -u`. Fixed. All four configs then matched: + 4930 / 4924 / 4934 / 4928 for both Zig-linked and C-linked. +6. Host nesting cliff for C `te_interp` is crash at depth 23764 (ulimit -s + 8176). First machine reported 20144 on Linux. Same bug, different stack + geometry; both numbers belong in the record. +7. The README claim script was not a real gate: it re-rendered the README + before checking, so a planted 4999/4999 still exited 0. We removed the + silent rewrite and proved the new path fails. Escape-hatch, hash, and fmt + gates were also watched failing under temporary mutations. + +Licence/pin review on this machine: zlib origin / altered-mark / notice retain +look satisfied in LICENSE, THIRD_PARTY.md, and LICENSE-NOTICE.txt; +`.port-mortem.toml` carries track G, URL, pin, and hashes. + +Full table: `docs/REPRODUCTION.md`. Gate experiments: +`docs/evidence/gate-failure-proofs.md`. + From df8e8183f3eca65ef0189332ae89f138fe5f995c Mon Sep 17 00:00:00 2001 From: kavyabhand Date: Sun, 2 Aug 2026 15:52:48 +0530 Subject: [PATCH 5/5] ci: drop dockerfile syntax frontend pull CI docker-build-and-verify timed out resolving docker/dockerfile:1.7 from registry-1.docker.io. The image does not need that frontend. --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d2ee8c0..2a54db6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,10 @@ -# syntax=docker/dockerfile:1.7 # Reproducible verify image for tinyexpr.zig # Zig tarball pinned by SHA-256 per architecture. Base is ubuntu:24.04. # TARGETARCH comes from BuildKit (amd64 / arm64). Without this, Apple Silicon # Docker builds an arm64 Ubuntu image, downloads the x86_64 Zig binary, and # fails at link time with "invalid ELF machine type: AARCH64". +# Note: no `# syntax=docker/dockerfile:...` line. That frontend pull has timed +# out on GitHub-hosted runners talking to registry-1.docker.io. FROM ubuntu:24.04 AS base ENV DEBIAN_FRONTEND=noninteractive