diff --git a/.github/workflows/thesis-facts-append.yml b/.github/workflows/thesis-facts-append.yml new file mode 100644 index 0000000..2685fbd --- /dev/null +++ b/.github/workflows/thesis-facts-append.yml @@ -0,0 +1,132 @@ +name: Thesis facts append gate + +# Deterministic review for every change to the observation ledger. Resolver +# appends arrive as pull requests targeting codex/thesis-ledger-facts; this +# gate enforces the immutable frozen prefix, an append-only diff against the +# PR base, the witnessed release chain, per-row schema and binding requirements, +# and explicit supersede semantics for corrections. Direct pushes get the same +# full-file checks. +# +# No paths filter, on purpose: branch protection lists "Append gate" as a +# required status check, so it must report on every pull request into the +# branch. With a paths filter, a PR that touches none of the gated files +# (tests, CI config) never receives the check and deadlocks on "Expected". +# For such PRs the append-only diff is trivially clean and the gate stays +# cheap. + +on: + pull_request: + branches: + - codex/thesis-ledger-facts + # When this definition is installed on the repository default branch, this + # base-owned event can be the trust root for a required PR workflow. The + # ordinary pull_request workflow below still tests the merge ref, but its YAML + # is candidate-controlled and cannot by itself prevent a PR from stubbing the + # job that invokes the detached base gate. No paths filter here either: a + # required check that a paths filter can skip deadlocks on "Expected" for + # out-of-scope PRs. + pull_request_target: + branches: + - codex/thesis-ledger-facts + push: + branches: + - codex/thesis-ledger-facts + +permissions: + contents: read + +jobs: + trusted-base-append-gate: + name: Trusted base append gate + if: github.event_name == 'pull_request_target' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Judge the PR merge tree with the base gate + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + [[ "$BASE_SHA" =~ ^[0-9a-f]{40,64}$ ]] + [[ "$MERGE_SHA" =~ ^[0-9a-f]{40,64}$ ]] + [[ "$PR_NUMBER" =~ ^[0-9]+$ ]] + + repository_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" + candidate="$RUNNER_TEMP/thesis-facts-candidate" + base_gate="$RUNNER_TEMP/thesis-facts-base-gate" + + git clone --no-checkout "$repository_url" "$candidate" + git -C "$candidate" fetch --no-tags origin \ + "+refs/pull/${PR_NUMBER}/merge:refs/remotes/origin/pr-merge" + git -C "$candidate" checkout --detach refs/remotes/origin/pr-merge + test "$(git -C "$candidate" rev-parse HEAD)" = "$MERGE_SHA" + git -C "$candidate" cat-file -e "${BASE_SHA}^{commit}" + + git clone --no-checkout "$repository_url" "$base_gate" + git -C "$base_gate" fetch --no-tags origin "$BASE_SHA" + git -C "$base_gate" checkout --detach "$BASE_SHA" + test "$(git -C "$base_gate" rev-parse HEAD)" = "$BASE_SHA" + + cd "$RUNNER_TEMP" + PYTHONPATH="$base_gate/scripts" \ + PYTHONNOUSERSITE=1 \ + python3 "$base_gate/scripts/check_thesis_facts_append.py" \ + --root "$candidate" \ + --base-ref "$BASE_SHA" + + append-gate: + name: Append gate + if: github.event_name != 'pull_request_target' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Enforce append-only ledger invariants + run: | + set -euo pipefail + if [ "${{ github.event_name }}" = "pull_request" ]; then + base_sha="${{ github.event.pull_request.base.sha }}" + base_gate="$RUNNER_TEMP/thesis-facts-base-gate" + + git -C "$GITHUB_WORKSPACE" cat-file -e "${base_sha}^{commit}" + git clone --no-checkout \ + "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" \ + "$base_gate" + git -C "$base_gate" fetch --no-tags --depth 1 origin "$base_sha" + git -C "$base_gate" checkout --detach "$base_sha" + test "$(git -C "$base_gate" rev-parse HEAD)" = "$base_sha" + + cd "$RUNNER_TEMP" + PYTHONPATH="$base_gate/scripts" \ + PYTHONNOUSERSITE=1 \ + python3 "$base_gate/scripts/check_thesis_facts_append.py" \ + --root "$GITHUB_WORKSPACE" \ + --base-ref "$base_sha" + else + python3 scripts/check_thesis_facts_append.py + fi + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install dependencies + run: uv sync --locked --all-extras + + - name: Ledger observation invariants + run: > + uv run pytest + tests/test_policyengine_ledger.py + tests/test_release_chain.py + tests/test_thesis_append_adversarial.py + -q