Artifact 7ab4dc3ba8022bf3ac86e0a44a9a5a2df8bdb42afa629acab5a69ef53064f55b:
- File
.github/workflows/ci.yml
— part of check-in
[dcd6c48df9]
at
2026-08-06 01:33:40
on branch sub-manifest
— citations: re-key 20 against the corrected sections
d4e0e0d6da fixed six renumbered sections and deliberately left their line ranges stale, on the reasoning that a correct section turns a stale range into an ordinary lookup. This is that lookup: 20 of 100 survived review.
The 20 percent hit rate is lower than the pass before it, and the reason is worth recording. It is NOT that the renumbering targets were wrong -- §11.1 L2604-2607 reads identically before and after that commit, so those citations were never renumbered by it. What the pass found instead is a second, older layer of mis-sectioning underneath the first: claims about server-emitted error cards citing §11.1 Credential Replay when they belong in §10.1 Error Card. Fixing one generation of drift exposed the next.
Two refusals name failure modes the applier could not have caught. One proposal re-keyed half of a COMPOUND citation -- sync.zig:961 carries `§9.3 L3278/L3320-3322` -- which would have welded a freshly verified range to a still-rotted one on the same line. Another submitted a quote that stopped mid-word, with the half of the claim it needed sitting in the sentence it cut off.
The 66 unresolved are now the honest core: three independent adversarial passes have each declined to invent an answer for them.
Baseline 100 to 80; 90 percent of citations now resolve. test 3495/3495, fmt. Issue #18. (user: dmestas, size: 9071) [annotate] [blame] [check-ins using]
name: ci # Every push on every branch, plus pull requests. Deliberately unfiltered: the # 0.1.0 release is developed on `release-0.1`, and a default-branch-only gate # would leave the entire release unverified until the merge that is supposed to # be the safe part. on: push: pull_request: # Least privilege: the battery only reads the checkout. Without an explicit # block the job inherits whatever the repo/org default grants, which is # read/write on many configurations. permissions: contents: read # Unfiltered `push:` plus `pull_request:` means a PR branch runs the whole # battery twice per push. Superseded runs are cancelled rather than left to # finish against a commit nobody is waiting on. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # One release gate across the native macOS/ARM64 and Linux x86-64/ARM64 # platforms. Each label set is intentionally precise: Linux legs remain queued # until the corresponding self-hosted runner is registered; they never fall # back to a hosted or arbitrary self-hosted machine. jobs: battery: strategy: fail-fast: false matrix: include: - os: macOS arch: ARM64 runner: mac-mini-zit zig_target: aarch64-macos zig_shasum: b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489 - os: Linux arch: X64 runner: linux-x64-zit zig_target: x86_64-linux zig_shasum: 70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00 - os: Linux arch: ARM64 runner: linux-arm64-zit zig_target: aarch64-linux zig_shasum: ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17 runs-on: [self-hosted, "${{ matrix.os }}", "${{ matrix.arch }}", "${{ matrix.runner }}"] # An explicit ceiling, per the bounded-waits rule: every wait needs an # interval and a timeout. Without this the job inherits GitHub's 360-minute # default, so a hang — most plausibly smoke's spawned server never exiting — # turns a red gate into a six-hour silent stall instead of a failure. timeout-minutes: 20 env: # Kept in lockstep with src/ci_test.zig's `pinned_zig_version` and # `pinned_zig_shasums`, which fail `zig build test` if this file drifts. # These are the target-specific published digests from # ziglang.org/download/index.json. ZIG_VERSION: 0.16.0 ZIG_TARGET: ${{ matrix.zig_target }} ZIG_SHASUM: ${{ matrix.zig_shasum }} steps: - uses: actions/checkout@v5 # A `§X.Y Lnnn` citation that resolves to the wrong paragraph is worse # than no citation: it looks authoritative, so the next person checking a # normative claim against it is told the wrong thing. Line numbers rot # every time a draft is re-rendered to apply an erratum, and nothing # noticed until a sweep found most of them wrong. # # A RATCHET, not a gate on zero: the backlog is real and is being worked # down, so this pins the count and fails in BOTH directions -- a new # broken citation, or a fixed one whose baseline was not lowered. Needs # no toolchain, so it runs before the Zig install and fails fast. - name: Check draft citations run: python3 scripts/check-citations.py --baseline 80 # smoke drives the real fossil binary — it is the only step that proves # the conformance claim against the reference implementation rather than # against our own reading of the RFCs. Fossil is provisioned by each # self-hosted runner image, so print the actual version used by the gate. - name: Verify fossil run: | set -euo pipefail command -v fossil >/dev/null 2>&1 || { echo "fossil is not installed on this runner; smoke cannot run." >&2 echo "provision fossil in this self-hosted runner image." >&2 exit 1 } fossil version # Installed by hand rather than through a setup action: the toolchain is # one tarball, the URL is stable, and verifying the published digest here # is both simpler and stronger than trusting a third-party action to pin # the same thing. `sha256sum` is standard on Linux; macOS supplies # `shasum`, so select the available native checksum tool. - name: Install Zig ${{ env.ZIG_VERSION }} run: | set -euo pipefail tarball="zig-${ZIG_TARGET}-${ZIG_VERSION}.tar.xz" curl -fsSL -o "$tarball" "https://ziglang.org/download/${ZIG_VERSION}/${tarball}" if command -v sha256sum >/dev/null 2>&1; then actual="$(sha256sum "$tarball" | awk '{print $1}')" else actual="$(shasum -a 256 "$tarball" | awk '{print $1}')" fi if [ "$actual" != "${ZIG_SHASUM}" ]; then echo "zig tarball digest mismatch: expected ${ZIG_SHASUM}, got ${actual}" >&2 exit 1 fi tar -xf "$tarball" echo "${GITHUB_WORKSPACE}/zig-${ZIG_TARGET}-${ZIG_VERSION}" >> "$GITHUB_PATH" - name: Zig version run: zig version # `--check build.zig src` rather than `--check .`: the bare dot walks # .zig-cache/ and zig-out/, whose generated sources are not ours to # format. Naming the source roots keeps the check meaning the same thing # on a cold runner and in a warm local tree. - name: Format check run: zig fmt --check build.zig src - name: Build run: zig build # The battery runs each step explicitly. Local semantics are deliberately # unchanged: e2e staying compile-only under `zig build test` is a good # inner-loop design, so CI names the steps rather than the build graph # being rewired to fold them in. - name: Unit tests run: zig build test - name: End-to-end tests run: zig build e2e - name: Native Git import differential tests timeout-minutes: 5 run: zig build git-import-e2e - name: Deterministic simulation run: zig build dst # The seeded fuzz corpus, bounded. The targets also reach `zig build test` # through fixed-seed smoke cases, but this step exercises every registered # target through the real fuzz runner. # # `zig build fuzz` runs ONE target per invocation, so this sweeps them. # `--list` exits successfully and writes the complete registration table; # unlike scraping a failed bare invocation, its output is not truncated by # Zig's build-failure summary on Linux. A target added to fuzz_tests.zig is # therefore fuzzed automatically, with no second list to drift out of sync. # # Seed and rounds are fixed, not random: a red CI run has to be # reproducible verbatim on a developer machine. Deeper sweeps belong to # the out-of-band runner with its own budget. - name: Seeded fuzz sweep timeout-minutes: 10 env: FUZZ_SEED: 1 # 100, not 2000. Per-target cost varies by ~276x across the corpus — # artifact_parse_garbage runs 100 rounds in 0.18s while # logstore_pack_same_path_round_trip takes 49s, because it performs a # full store pack per round. The original 2000 was extrapolated from # one cheap target and made the sweep unable to finish inside this # step's own timeout: that single target alone would need ~16 minutes. # Measured at 100 rounds the whole sweep is ~76s, which is the budget # this number is chosen against, not a guess. # # What this buys is REGRESSION DETECTION — every target still runs # clean on every push. It is not a bug hunt: finding new defects needs # depth, and depth belongs in an out-of-band run with its own budget, # where a 30-minute sweep costs nobody a merge. FUZZ_ROUNDS: 100 run: | set -euo pipefail targets="$(zig build fuzz -- --list)" if [ -z "${targets}" ]; then echo "no fuzz targets enumerated — the usage format changed and this sweep is vacuous" >&2 exit 1 fi count=0 for target in ${targets}; do echo "fuzz: ${target} (seed ${FUZZ_SEED}, rounds ${FUZZ_ROUNDS})" zig build fuzz -- "${target}" --seed "${FUZZ_SEED}" --rounds "${FUZZ_ROUNDS}" count=$((count + 1)) done echo "fuzz sweep: ${count} targets at seed ${FUZZ_SEED}" # The tightest per-step bound in the battery, and deliberately so: smoke # spawns a server, which is the one step that can hang without producing # output. The job ceiling would eventually catch it; this catches it while # the log still reads as a failed step rather than a dead run. - name: Fossil interop smoke timeout-minutes: 5 run: zig build smoke