From a00238fbc086d31cb96c32bd59a6c2e0641093fe Mon Sep 17 00:00:00 2001 From: Victor Gutierrez Calderon Date: Fri, 31 Jul 2026 12:26:02 +0200 Subject: [PATCH 1/4] ci: add daily checkpoint-availability audit job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a scheduled audit that catches checkpoints referenced by commit trailers but missing from the checkpoint remote — the failure behind `entire trail resume` reporting "checkpoint not found" when a git-refs checkpoint ref reached origin as a commit trailer but was never pushed to github.com/entireio/cli-checkpoints. - scripts/checkpoint-audit.sh: walks all-branch commits in a window, extracts Entire-Checkpoint trailers via git's own trailer formatting, and diffs them against `git ls-remote refs/entire/checkpoints/*`. Exit 0 = all present, 1 = missing (with a context table: checkpoint, commit, author, branch, date, subject), 2 = remote/setup error so a broken remote never reads as "0 missing". RFC 7617 token auth matches the CLI (ENTIRE_CHECKPOINT_TOKEN), never creds-in-URL. - .github/workflows/checkpoint-audit.yml: daily 07:00 UTC cron (plus workflow_dispatch). Fails the job on missing checkpoints and reports via GITHUB_STEP_SUMMARY, an uploaded artifact, and Slack. - mise.toml: `checkpoint:audit` task for local runs. Co-Authored-By: Claude Opus 4.8 (1M context) Entire-Checkpoint: 01KYVVBETH3GTRBRPKWVQBN0JK --- .github/workflows/checkpoint-audit.yml | 85 +++++++++++++ mise.toml | 4 + scripts/checkpoint-audit.sh | 161 +++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 .github/workflows/checkpoint-audit.yml create mode 100755 scripts/checkpoint-audit.sh diff --git a/.github/workflows/checkpoint-audit.yml b/.github/workflows/checkpoint-audit.yml new file mode 100644 index 0000000000..df8cd091d0 --- /dev/null +++ b/.github/workflows/checkpoint-audit.yml @@ -0,0 +1,85 @@ +name: Checkpoint Audit + +on: + schedule: + - cron: "0 7 * * *" # 07:00 UTC daily (GitHub does not honor sub-daily cron reliably) + workflow_dispatch: + inputs: + window: + description: 'git --since window to audit' + default: '24 hours ago' + +permissions: + contents: read + +concurrency: + group: checkpoint-audit-${{ github.ref }} + cancel-in-progress: true + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - name: Generate checkpoint-repo token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.HOMEBREW_TAP_APP_ID }} + private-key: ${{ secrets.HOMEBREW_TAP_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: cli-checkpoints + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Fetch all branches + run: git fetch --all --prune + + - name: Run checkpoint audit + id: audit + continue-on-error: true + env: + ENTIRE_CHECKPOINT_TOKEN: ${{ steps.app-token.outputs.token }} + AUDIT_WINDOW: ${{ github.event.inputs.window || '24 hours ago' }} + run: scripts/checkpoint-audit.sh + + - name: Upload audit report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: checkpoint-audit-report + path: | + checkpoint-audit-report.md + checkpoint-audit-report.json + retention-days: 7 + + - name: Notify Slack of missing checkpoints + if: ${{ always() && steps.audit.outcome == 'failure' }} + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 + with: + webhook: ${{ secrets.E2E_SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + { + "attachments": [ + { + "color": "#d50200", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":red_circle: *Checkpoint audit found missing checkpoints*\n\nOne or more commits reference a checkpoint that is not on the checkpoint remote. See the report artifact for the full table.\n\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run details>" + } + } + ] + } + ] + } + + - name: Fail if checkpoints are missing + if: ${{ steps.audit.outcome == 'failure' }} + run: | + echo "::error::checkpoint audit reported missing checkpoints" + exit 1 diff --git a/mise.toml b/mise.toml index a9dfb22166..34d9f02fb1 100644 --- a/mise.toml +++ b/mise.toml @@ -30,6 +30,10 @@ mise run test:e2e:canary description = "Run formatting, linting, and CI tests" depends = ["fmt", "lint", "test:ci"] +[tasks."checkpoint:audit"] +description = "Audit that recent commits' checkpoints exist on the checkpoint remote (set ENTIRE_CHECKPOINT_TOKEN)" +run = "scripts/checkpoint-audit.sh" + [tasks."build:windows"] description = "Cross-compile for Windows amd64" run = "CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o entire.exe ./cmd/entire/" diff --git a/scripts/checkpoint-audit.sh b/scripts/checkpoint-audit.sh new file mode 100755 index 0000000000..1556b64edb --- /dev/null +++ b/scripts/checkpoint-audit.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Audit checkpoint availability on the checkpoint remote. +# +# Walks every commit created within a time window across all branches, extracts +# each `Entire-Checkpoint:` trailer, and checks whether the matching checkpoint +# ref exists on the checkpoint remote (github.com/). Any commit +# whose checkpoint ref is missing from the remote is reported with context and +# makes the script exit non-zero. +# +# This catches the failure mode where a commit reaches origin but its git-refs +# checkpoint ref (refs/entire/checkpoints//) was never pushed to the +# checkpoint remote — the checkpoint is then unrecoverable on any other machine +# (e.g. `entire trail resume` / `entire explain` fail with "checkpoint not found"). +# +# Trailer parsing and ref-set membership use git's own primitives rather than +# hand-rolled regex/shard math: `git show --format='%(trailers:...)'` parses +# trailers (handling squash-merge commits with multiple checkpoint trailers), and +# the checkpoint ID is simply the ref leaf, so membership is a plain string-set +# lookup that works for both legacy-hex and ULID IDs. +# +# Env (all optional except the token for a private remote): +# ENTIRE_CHECKPOINT_TOKEN GitHub token with read access to the checkpoint repo. +# Sent as an RFC 7617 basic auth header (matching the +# CLI), so it never appears in a remote URL. +# CHECKPOINT_REPO owner/repo of the checkpoint remote +# (default: entireio/cli-checkpoints). +# AUDIT_WINDOW git `--since` window (default: "24 hours ago"). +# AUDIT_REPORT_FILE markdown report sink (default: checkpoint-audit-report.md). +# AUDIT_JSON_FILE machine-readable report sink (default: checkpoint-audit-report.json). +# GITHUB_STEP_SUMMARY if set, the markdown report is appended to it. +# +# Exit codes: 0 = all present, 1 = one or more checkpoints missing, 2 = setup or +# remote error (a broken remote must never read as "0 missing"). + +CHECKPOINT_REPO="${CHECKPOINT_REPO:-entireio/cli-checkpoints}" +AUDIT_WINDOW="${AUDIT_WINDOW:-24 hours ago}" +AUDIT_REPORT_FILE="${AUDIT_REPORT_FILE:-checkpoint-audit-report.md}" +AUDIT_JSON_FILE="${AUDIT_JSON_FILE:-checkpoint-audit-report.json}" +TOKEN="${ENTIRE_CHECKPOINT_TOKEN:-}" + +CHECKPOINT_URL="https://github.com/${CHECKPOINT_REPO}.git" + +REMOTE_IDS_FILE=$(mktemp) +ROWS_FILE=$(mktemp) +trap 'rm -f "$REMOTE_IDS_FILE" "$ROWS_FILE"' EXIT + +# json_str emits a JSON string literal, escaping backslashes and double quotes. +# Author names and subjects are single-line, so control-char escaping is not needed. +json_str() { + local s=${1//\\/\\\\} + s=${s//\"/\\\"} + printf '"%s"' "$s" +} + +# 1. Enumerate the checkpoint refs present on the remote (names only, no object +# transfer). The ID is the ref leaf: refs/entire/checkpoints//. +echo "Enumerating checkpoint refs on ${CHECKPOINT_REPO} ..." >&2 +if [ -n "$TOKEN" ]; then + auth_b64=$(printf 'x-access-token:%s' "$TOKEN" | base64 | tr -d '\n') + remote_refs=$(git -c "http.extraheader=AUTHORIZATION: basic ${auth_b64}" \ + ls-remote "$CHECKPOINT_URL" 'refs/entire/checkpoints/*') || { + echo "::error::failed to ls-remote ${CHECKPOINT_REPO} (check ENTIRE_CHECKPOINT_TOKEN and repo access)" >&2 + exit 2 + } +else + remote_refs=$(git ls-remote "$CHECKPOINT_URL" 'refs/entire/checkpoints/*') || { + echo "::error::failed to ls-remote ${CHECKPOINT_REPO} (no ENTIRE_CHECKPOINT_TOKEN set; is the repo private?)" >&2 + exit 2 + } +fi + +printf '%s\n' "$remote_refs" \ + | awk '$2 ~ /^refs\/entire\/checkpoints\// { id = $2; sub(/.*\//, "", id); print id }' \ + | sort -u > "$REMOTE_IDS_FILE" +remote_count=$(grep -c . "$REMOTE_IDS_FILE" || true) + +# 2. Candidate commits: every branch commit in the window (local heads + remotes), +# de-duplicated while preserving order. +commits=$(git log --branches --remotes --since="$AUDIT_WINDOW" --format='%H' | awk '!seen[$0]++') + +# 3. For each commit, diff its checkpoint trailers against the remote set. +commit_count=0 +cp_count=0 +missing_count=0 +while IFS= read -r sha; do + [ -z "$sha" ] && continue + commit_count=$((commit_count + 1)) + cps=$(git show -s --format='%(trailers:key=Entire-Checkpoint,valueonly=true)' "$sha") + while IFS= read -r cp; do + cp="${cp//[[:space:]]/}" + [ -z "$cp" ] && continue + cp_count=$((cp_count + 1)) + if grep -Fxq "$cp" "$REMOTE_IDS_FILE"; then + continue + fi + missing_count=$((missing_count + 1)) + meta=$(git show -s --format='%h%x1f%an%x1f%aI%x1f%s' "$sha") + short=${meta%%$'\x1f'*}; meta=${meta#*$'\x1f'} + author=${meta%%$'\x1f'*}; meta=${meta#*$'\x1f'} + cdate=${meta%%$'\x1f'*}; subject=${meta#*$'\x1f'} + branches=$(git branch -a --contains "$sha" --format='%(refname:short)' 2>/dev/null \ + | sed -e 's#^remotes/##' -e 's#^origin/##' \ + | grep -v '^entire/' \ + | awk 'NF && !s[$0]++' \ + | paste -sd', ' -) + printf '%s\x1e%s\x1e%s\x1e%s\x1e%s\x1e%s\n' \ + "$cp" "$short" "$author" "${branches:-?}" "$cdate" "$subject" >> "$ROWS_FILE" + done <> "$GITHUB_STEP_SUMMARY" +fi + +# 5. Machine-readable report for the artifact. +{ + echo "[" + first=1 + while IFS=$'\x1e' read -r cp short author branches cdate subject; do + if [ "$first" -eq 1 ]; then first=0; else echo ","; fi + printf ' {"checkpoint":%s,"commit":%s,"author":%s,"branches":%s,"date":%s,"subject":%s}' \ + "$(json_str "$cp")" "$(json_str "$short")" "$(json_str "$author")" \ + "$(json_str "$branches")" "$(json_str "$cdate")" "$(json_str "$subject")" + done < "$ROWS_FILE" + echo + echo "]" +} > "$AUDIT_JSON_FILE" + +if [ "$missing_count" -gt 0 ]; then + echo "::error::${missing_count} checkpoint(s) missing from ${CHECKPOINT_REPO}" >&2 + exit 1 +fi +echo "All ${cp_count} checkpoint(s) present on ${CHECKPOINT_REPO}." >&2 +exit 0 From f7df39107425825f5aac6399e5483a1739f89655 Mon Sep 17 00:00:00 2001 From: Victor Gutierrez Calderon Date: Fri, 31 Jul 2026 12:34:42 +0200 Subject: [PATCH 2/4] =?UTF-8?q?ci(checkpoint-audit):=20drop=20App=20token?= =?UTF-8?q?=20=E2=80=94=20checkpoint=20remote=20is=20public?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit entireio/cli-checkpoints is a public repo, so the audit's read-only `git ls-remote` needs no credentials. Remove the create-github-app-token step and ENTIRE_CHECKPOINT_TOKEN wiring; anonymous HTTPS fetch is enough. The script still honors ENTIRE_CHECKPOINT_TOKEN if the remote ever goes private. Co-Authored-By: Claude Opus 4.8 (1M context) Entire-Checkpoint: 01KYVVVJ8N5XPJVRTPD400B67N --- .github/workflows/checkpoint-audit.yml | 13 +++---------- checkpoint-audit-report.json | 5 +++++ checkpoint-audit-report.md | 12 ++++++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 checkpoint-audit-report.json create mode 100644 checkpoint-audit-report.md diff --git a/.github/workflows/checkpoint-audit.yml b/.github/workflows/checkpoint-audit.yml index df8cd091d0..df9d6010b7 100644 --- a/.github/workflows/checkpoint-audit.yml +++ b/.github/workflows/checkpoint-audit.yml @@ -20,15 +20,9 @@ jobs: audit: runs-on: ubuntu-latest steps: - - name: Generate checkpoint-repo token - id: app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - app-id: ${{ secrets.HOMEBREW_TAP_APP_ID }} - private-key: ${{ secrets.HOMEBREW_TAP_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: cli-checkpoints - + # The checkpoint remote (entireio/cli-checkpoints) is public, so the audit's + # read-only `git ls-remote` needs no credentials. If it is ever made private, + # set ENTIRE_CHECKPOINT_TOKEN on the audit step from a token with read access. - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 @@ -40,7 +34,6 @@ jobs: id: audit continue-on-error: true env: - ENTIRE_CHECKPOINT_TOKEN: ${{ steps.app-token.outputs.token }} AUDIT_WINDOW: ${{ github.event.inputs.window || '24 hours ago' }} run: scripts/checkpoint-audit.sh diff --git a/checkpoint-audit-report.json b/checkpoint-audit-report.json new file mode 100644 index 0000000000..e8f590877a --- /dev/null +++ b/checkpoint-audit-report.json @@ -0,0 +1,5 @@ +[ + {"checkpoint":"01KYVVES1A3F6DT85KWNM01CXW","commit":"7d26969d4","author":"Victor Gutierrez Calderon","branches":"feat/push-progress-quiet","date":"2026-07-31T12:27:53+02:00","subject":"feat(push): pure reveal-threshold decision for pre-push progress"}, + {"checkpoint":"01KYVVE0SJQC7MHKJKYK4QRSDJ","commit":"820de5985","author":"Victor Gutierrez Calderon","branches":"feat/status-mirror","date":"2026-07-31T11:54:54+02:00","subject":"feat(status): surface whether the current clone points at a mirror"}, + {"checkpoint":"01KYSGR4W1ZATZRW47J510EZGJ","commit":"4e229c054","author":"Matthias Wenz","branches":"feat/repo-mirror-use,trail-resume-does-not-find-checkpoint","date":"2026-07-30T14:42:18+02:00","subject":"feat(repo): add `entire repo mirror use` to repoint a clone at a mirror"} +] diff --git a/checkpoint-audit-report.md b/checkpoint-audit-report.md new file mode 100644 index 0000000000..caaeb4d438 --- /dev/null +++ b/checkpoint-audit-report.md @@ -0,0 +1,12 @@ +# Checkpoint availability audit + +- Window: commits since `24 hours ago` +- Checkpoint remote: `entireio/cli-checkpoints` (384 refs present) +- Scanned: 35 commit(s), 15 checkpoint trailer(s) +- **Missing from remote: 3** + +| Checkpoint | Commit | Author | Branch(es) | Date | Subject | +|---|---|---|---|---|---| +| `01KYVVES1A3F6DT85KWNM01CXW` | `7d26969d4` | Victor Gutierrez Calderon | feat/push-progress-quiet | 2026-07-31T12:27:53+02:00 | feat(push): pure reveal-threshold decision for pre-push progress | +| `01KYVVE0SJQC7MHKJKYK4QRSDJ` | `820de5985` | Victor Gutierrez Calderon | feat/status-mirror | 2026-07-31T11:54:54+02:00 | feat(status): surface whether the current clone points at a mirror | +| `01KYSGR4W1ZATZRW47J510EZGJ` | `4e229c054` | Matthias Wenz | feat/repo-mirror-use,trail-resume-does-not-find-checkpoint | 2026-07-30T14:42:18+02:00 | feat(repo): add `entire repo mirror use` to repoint a clone at a mirror | From e33365f6b7c73111f30c3eabbda217effe27830f Mon Sep 17 00:00:00 2001 From: Victor Gutierrez Calderon Date: Fri, 31 Jul 2026 12:46:48 +0200 Subject: [PATCH 3/4] ci(checkpoint-audit): address review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bot review (Cursor Bugbot + Copilot) on the audit script and workflow: - Branch lookup no longer aborts the run: the `git branch --contains` pipeline gets `|| true` so grep exiting 1 (all branches filtered, or none) under `set -e` can't kill the script before the report renders. - Multi-branch cells join with ", " — `paste -sd', '` treated the delimiter as a circular char list and alternated "," and " ". - git-log failure (e.g. run outside a repo) now exits 2 with a clear message instead of leaking git's raw 128. - json_str escapes tab and the other C0 controls a commit subject can legally carry, so the JSON report is always valid. - mktemp uses a portable ${TMPDIR:-/tmp}/…XXXXXX template (BSD/macOS). - Workflow captures the script's exit code and distinguishes missing (1) from remote/setup error (2): the Slack alert no longer claims "missing checkpoints" when the audit simply couldn't run. upload-artifact gets if-no-files-found: ignore for the no-report error path. - Stop tracking the runtime report artifacts and gitignore them (they were committed by an earlier `git add -A`). Co-Authored-By: Claude Opus 4.8 (1M context) Entire-Checkpoint: 01KYVWHKDPJH5WYMANGZ9SPF4Z --- .github/workflows/checkpoint-audit.yml | 36 ++++++++++++++++++++------ .gitignore | 4 +++ checkpoint-audit-report.json | 5 ---- checkpoint-audit-report.md | 12 --------- scripts/checkpoint-audit.sh | 31 +++++++++++++++++----- 5 files changed, 56 insertions(+), 32 deletions(-) delete mode 100644 checkpoint-audit-report.json delete mode 100644 checkpoint-audit-report.md diff --git a/.github/workflows/checkpoint-audit.yml b/.github/workflows/checkpoint-audit.yml index df9d6010b7..0bf006bada 100644 --- a/.github/workflows/checkpoint-audit.yml +++ b/.github/workflows/checkpoint-audit.yml @@ -30,12 +30,18 @@ jobs: - name: Fetch all branches run: git fetch --all --prune + # Capture the script's exit code (0 = all present, 1 = missing, 2 = + # remote/setup error) without failing the step, so the report, Slack, and + # gate steps below can distinguish "missing" from "could not run". - name: Run checkpoint audit id: audit - continue-on-error: true env: AUDIT_WINDOW: ${{ github.event.inputs.window || '24 hours ago' }} - run: scripts/checkpoint-audit.sh + run: | + set +e + scripts/checkpoint-audit.sh + echo "code=$?" >> "$GITHUB_OUTPUT" + exit 0 - name: Upload audit report if: always() @@ -46,9 +52,23 @@ jobs: checkpoint-audit-report.md checkpoint-audit-report.json retention-days: 7 + if-no-files-found: ignore + + # Distinct wording per outcome: exit 1 asserts missing checkpoints (a report + # artifact exists); exit 2 is a remote/setup failure with no report, so the + # alert must not claim checkpoints are missing (avoids false positives). + - name: Compose Slack message + id: msg + if: ${{ always() && steps.audit.outputs.code != '0' }} + run: | + if [ "${{ steps.audit.outputs.code }}" = "1" ]; then + echo 'text=:red_circle: *Checkpoint audit: missing checkpoints*\n\nOne or more commits reference a checkpoint that is not on the checkpoint remote. See the report artifact for the full table.' >> "$GITHUB_OUTPUT" + else + echo 'text=:red_circle: *Checkpoint audit failed to run*\n\nThe audit could not complete (remote or setup error); no report was produced. See the run log.' >> "$GITHUB_OUTPUT" + fi - - name: Notify Slack of missing checkpoints - if: ${{ always() && steps.audit.outcome == 'failure' }} + - name: Notify Slack + if: ${{ always() && steps.audit.outputs.code != '0' }} uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 with: webhook: ${{ secrets.E2E_SLACK_WEBHOOK_URL }} @@ -63,7 +83,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": ":red_circle: *Checkpoint audit found missing checkpoints*\n\nOne or more commits reference a checkpoint that is not on the checkpoint remote. See the report artifact for the full table.\n\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run details>" + "text": "${{ steps.msg.outputs.text }}\n\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run details>" } } ] @@ -71,8 +91,8 @@ jobs: ] } - - name: Fail if checkpoints are missing - if: ${{ steps.audit.outcome == 'failure' }} + - name: Fail the job on audit error + if: ${{ steps.audit.outputs.code != '0' }} run: | - echo "::error::checkpoint audit reported missing checkpoints" + echo "::error::checkpoint audit exited ${{ steps.audit.outputs.code }} (1 = missing checkpoints, 2 = remote/setup error)" exit 1 diff --git a/.gitignore b/.gitignore index 0ec1a656d8..543e8b2142 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,7 @@ tmp/ entire-external-cp-test/ /skills/ /git-remote-entire + +# Runtime output from scripts/checkpoint-audit.sh (never commit) +checkpoint-audit-report.md +checkpoint-audit-report.json diff --git a/checkpoint-audit-report.json b/checkpoint-audit-report.json deleted file mode 100644 index e8f590877a..0000000000 --- a/checkpoint-audit-report.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - {"checkpoint":"01KYVVES1A3F6DT85KWNM01CXW","commit":"7d26969d4","author":"Victor Gutierrez Calderon","branches":"feat/push-progress-quiet","date":"2026-07-31T12:27:53+02:00","subject":"feat(push): pure reveal-threshold decision for pre-push progress"}, - {"checkpoint":"01KYVVE0SJQC7MHKJKYK4QRSDJ","commit":"820de5985","author":"Victor Gutierrez Calderon","branches":"feat/status-mirror","date":"2026-07-31T11:54:54+02:00","subject":"feat(status): surface whether the current clone points at a mirror"}, - {"checkpoint":"01KYSGR4W1ZATZRW47J510EZGJ","commit":"4e229c054","author":"Matthias Wenz","branches":"feat/repo-mirror-use,trail-resume-does-not-find-checkpoint","date":"2026-07-30T14:42:18+02:00","subject":"feat(repo): add `entire repo mirror use` to repoint a clone at a mirror"} -] diff --git a/checkpoint-audit-report.md b/checkpoint-audit-report.md deleted file mode 100644 index caaeb4d438..0000000000 --- a/checkpoint-audit-report.md +++ /dev/null @@ -1,12 +0,0 @@ -# Checkpoint availability audit - -- Window: commits since `24 hours ago` -- Checkpoint remote: `entireio/cli-checkpoints` (384 refs present) -- Scanned: 35 commit(s), 15 checkpoint trailer(s) -- **Missing from remote: 3** - -| Checkpoint | Commit | Author | Branch(es) | Date | Subject | -|---|---|---|---|---|---| -| `01KYVVES1A3F6DT85KWNM01CXW` | `7d26969d4` | Victor Gutierrez Calderon | feat/push-progress-quiet | 2026-07-31T12:27:53+02:00 | feat(push): pure reveal-threshold decision for pre-push progress | -| `01KYVVE0SJQC7MHKJKYK4QRSDJ` | `820de5985` | Victor Gutierrez Calderon | feat/status-mirror | 2026-07-31T11:54:54+02:00 | feat(status): surface whether the current clone points at a mirror | -| `01KYSGR4W1ZATZRW47J510EZGJ` | `4e229c054` | Matthias Wenz | feat/repo-mirror-use,trail-resume-does-not-find-checkpoint | 2026-07-30T14:42:18+02:00 | feat(repo): add `entire repo mirror use` to repoint a clone at a mirror | diff --git a/scripts/checkpoint-audit.sh b/scripts/checkpoint-audit.sh index 1556b64edb..49ce038890 100755 --- a/scripts/checkpoint-audit.sh +++ b/scripts/checkpoint-audit.sh @@ -42,15 +42,21 @@ TOKEN="${ENTIRE_CHECKPOINT_TOKEN:-}" CHECKPOINT_URL="https://github.com/${CHECKPOINT_REPO}.git" -REMOTE_IDS_FILE=$(mktemp) -ROWS_FILE=$(mktemp) +REMOTE_IDS_FILE=$(mktemp "${TMPDIR:-/tmp}/checkpoint-audit.XXXXXX") +ROWS_FILE=$(mktemp "${TMPDIR:-/tmp}/checkpoint-audit.XXXXXX") trap 'rm -f "$REMOTE_IDS_FILE" "$ROWS_FILE"' EXIT -# json_str emits a JSON string literal, escaping backslashes and double quotes. -# Author names and subjects are single-line, so control-char escaping is not needed. +# json_str emits a JSON string literal. Git commit subjects can legally contain +# tabs and other control characters, so escape backslash, double quote, and the +# C0 controls that would otherwise produce invalid JSON. json_str() { local s=${1//\\/\\\\} s=${s//\"/\\\"} + s=${s//$'\t'/\\t} + s=${s//$'\r'/\\r} + s=${s//$'\n'/\\n} + s=${s//$'\b'/\\b} + s=${s//$'\f'/\\f} printf '"%s"' "$s" } @@ -77,8 +83,14 @@ printf '%s\n' "$remote_refs" \ remote_count=$(grep -c . "$REMOTE_IDS_FILE" || true) # 2. Candidate commits: every branch commit in the window (local heads + remotes), -# de-duplicated while preserving order. -commits=$(git log --branches --remotes --since="$AUDIT_WINDOW" --format='%H' | awk '!seen[$0]++') +# de-duplicated while preserving order. A git-log failure (e.g. run outside a +# git repo) is a setup error, not "0 missing" — surface it as exit 2 rather +# than letting git's raw 128 escape. +if ! commits=$(git log --branches --remotes --since="$AUDIT_WINDOW" --format='%H'); then + echo "::error::failed to enumerate commits (is this a git repository?)" >&2 + exit 2 +fi +commits=$(printf '%s\n' "$commits" | awk '!seen[$0]++') # 3. For each commit, diff its checkpoint trailers against the remote set. commit_count=0 @@ -100,11 +112,16 @@ while IFS= read -r sha; do short=${meta%%$'\x1f'*}; meta=${meta#*$'\x1f'} author=${meta%%$'\x1f'*}; meta=${meta#*$'\x1f'} cdate=${meta%%$'\x1f'*}; subject=${meta#*$'\x1f'} + # `|| true`: grep exits 1 when every branch is filtered out (or there are + # none), which would otherwise abort the whole run under `set -e`. Join with + # a single-char delimiter then expand to ", " — `paste -sd', '` treats the + # delimiter as a circular char list and would alternate "," and " ". branches=$(git branch -a --contains "$sha" --format='%(refname:short)' 2>/dev/null \ | sed -e 's#^remotes/##' -e 's#^origin/##' \ | grep -v '^entire/' \ | awk 'NF && !s[$0]++' \ - | paste -sd', ' -) + | paste -sd',' - \ + | sed 's/,/, /g' || true) printf '%s\x1e%s\x1e%s\x1e%s\x1e%s\x1e%s\n' \ "$cp" "$short" "$author" "${branches:-?}" "$cdate" "$subject" >> "$ROWS_FILE" done < Date: Mon, 3 Aug 2026 10:26:55 +0200 Subject: [PATCH 4/4] ci(checkpoint-audit): address second Copilot review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Token auth header uses standard casing `Authorization: Basic`, matching the CLI's implementation (checkpoint/remote/git.go) instead of the non-standard `AUTHORIZATION: basic`. - Markdown table escapes `|` in the author and branches cells too, not just the subject — author names are free-form and a pipe is a legal git refname character, either of which would break the table. - Workflow grants `actions: read` alongside `contents: read` so actions/upload-artifact works under a restricted token, matching e2e.yml / e2e-checkpoint-store.yml. Co-Authored-By: Claude Opus 4.8 (1M context) Entire-Checkpoint: 01KZ3BQF65CD8199EEWH1FFFH9 --- .github/workflows/checkpoint-audit.yml | 1 + scripts/checkpoint-audit.sh | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checkpoint-audit.yml b/.github/workflows/checkpoint-audit.yml index 0bf006bada..0aa1c21428 100644 --- a/.github/workflows/checkpoint-audit.yml +++ b/.github/workflows/checkpoint-audit.yml @@ -11,6 +11,7 @@ on: permissions: contents: read + actions: read # required for actions/upload-artifact under a restricted token (matches e2e.yml) concurrency: group: checkpoint-audit-${{ github.ref }} diff --git a/scripts/checkpoint-audit.sh b/scripts/checkpoint-audit.sh index 49ce038890..751c262934 100755 --- a/scripts/checkpoint-audit.sh +++ b/scripts/checkpoint-audit.sh @@ -65,7 +65,8 @@ json_str() { echo "Enumerating checkpoint refs on ${CHECKPOINT_REPO} ..." >&2 if [ -n "$TOKEN" ]; then auth_b64=$(printf 'x-access-token:%s' "$TOKEN" | base64 | tr -d '\n') - remote_refs=$(git -c "http.extraheader=AUTHORIZATION: basic ${auth_b64}" \ + # Standard header name/scheme casing, matching the CLI (Authorization: Basic). + remote_refs=$(git -c "http.extraheader=Authorization: Basic ${auth_b64}" \ ls-remote "$CHECKPOINT_URL" 'refs/entire/checkpoints/*') || { echo "::error::failed to ls-remote ${CHECKPOINT_REPO} (check ENTIRE_CHECKPOINT_TOKEN and repo access)" >&2 exit 2 @@ -144,6 +145,10 @@ EOF echo "| Checkpoint | Commit | Author | Branch(es) | Date | Subject |" echo "|---|---|---|---|---|---|" while IFS=$'\x1e' read -r cp short author branches cdate subject; do + # Escape every free-form cell: author names and (legal) branch names can + # contain a pipe, which would otherwise break the markdown table. + author=${author//|/\\|} + branches=${branches//|/\\|} subject=${subject//|/\\|} echo "| \`${cp}\` | \`${short}\` | ${author} | ${branches} | ${cdate} | ${subject} |" done < "$ROWS_FILE"