Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -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

-
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions .port-mortem.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
46 changes: 46 additions & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
29 changes: 20 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# 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".
# 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
Expand All @@ -19,14 +23,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
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 80 additions & 0 deletions docs/REPRODUCTION.md
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 19 additions & 0 deletions docs/evidence/gate-failure-proofs.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions docs/evidence/upstream-diff.txt
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions docs/journal.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Loading
Loading