Skip to content
Closed
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
455 changes: 455 additions & 0 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

600 changes: 600 additions & 0 deletions .github/fork/PROCESS.md

Large diffs are not rendered by default.

1,660 changes: 1,660 additions & 0 deletions .github/fork/forkctl.py

Large diffs are not rendered by default.

1,515 changes: 1,515 additions & 0 deletions .github/fork/test_forkctl.py

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!-- Fork PR into integration/3.0. Keep it short; the Fork gates check proves the rest. -->

## What

<!-- One or two sentences. If you need "and" for unrelated changes, split the PR. -->

## Why

<!-- The problem, or the capability we need. Link the issue if there is one. -->

## How tested

<!-- Only what CI cannot run for you. If CI covers it, write "CI" and stop.
Name anything you ran locally that CI does not: docker-smoke, perf, a GPU run,
a manual reproduction. A claim that a suite passed means it ran to completion. -->

New behaviour is covered by a new test / the fix has a test that fails without it:
<!-- name the test, or say why none applies -->

## Upstream disposition

- [ ] Mirror PR requested now — a human must verify title, body and diff first, and the body
carries the disclosure from `.github/fork/PROCESS.md` → *Agent authorship*
- [ ] Contributable, held for now because: <!-- reason -->
- [ ] **Never upstream** — fork-only `chore(fork):` change

<!-- Fork-only? Then ALL of: draft PR, [do-not-mirror][do-not-merge] in the title, both labels,
and the box above. Merging it would leave a second chore(fork) commit and abort every future
sync; it gets folded in by fixup instead. See PROCESS.md -> Changing fork-only content. -->

## Risk / notes

<!-- Behaviour, config or observation-layout changes, perf impact, follow-ups. Delete if none. -->
34 changes: 25 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
pull_request:
branches: [ main, '3.0' ]
branches: [ main, '3.0', 'integration/3.0' ]
push:
branches: [ main, '3.0' ]

Expand Down Expand Up @@ -33,8 +33,12 @@ jobs:
PIP_NO_CACHE_DIR: 1
run: |
sudo apt-get update && sudo apt-get install -y build-essential cmake
python -m pip install -U pip pytest
pip install -e . --no-cache-dir
python -m pip install -U pip "setuptools<81" wheel numpy pytest
# CPU-only torch: the CUDA stack no longer fits the runner's disk
# quota. --no-build-isolation then reuses it for the build instead
# of resolving a second copy. See tests/smoke_tests/Dockerfile.
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation --no-cache-dir
python setup.py build_ext --inplace --force

- name: Unit tests
Expand Down Expand Up @@ -70,8 +74,12 @@ jobs:
PIP_NO_CACHE_DIR: 1
run: |
sudo apt-get update && sudo apt-get install -y build-essential cmake xvfb libgl1-mesa-dev libegl1-mesa-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxxf86vm-dev
python -m pip install -U pip
pip install -e . --no-cache-dir
python -m pip install -U pip "setuptools<81" wheel numpy
# CPU-only torch: the CUDA stack no longer fits the runner's disk
# quota. --no-build-isolation then reuses it for the build instead
# of resolving a second copy. See tests/smoke_tests/Dockerfile.
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation --no-cache-dir
python setup.py build_ext --inplace --force

- name: C drive tests
Expand Down Expand Up @@ -103,8 +111,12 @@ jobs:
PIP_NO_CACHE_DIR: 1
run: |
sudo apt-get update && sudo apt-get install -y build-essential cmake
python -m pip install -U pip pytest jupytext nbclient ipykernel
pip install -e . --no-cache-dir
python -m pip install -U pip "setuptools<81" wheel numpy pytest jupytext nbclient ipykernel
# CPU-only torch: the CUDA stack no longer fits the runner's disk
# quota. --no-build-isolation then reuses it for the build instead
# of resolving a second copy. See tests/smoke_tests/Dockerfile.
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation --no-cache-dir
python setup.py build_ext --inplace --force

- name: Run notebooks
Expand Down Expand Up @@ -151,8 +163,12 @@ jobs:
PIP_NO_CACHE_DIR: 1
run: |
sudo apt-get update && sudo apt-get install -y build-essential cmake libgl1-mesa-dev libegl1-mesa-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxxf86vm-dev
python -m pip install -U pip
pip install -e . --no-cache-dir
python -m pip install -U pip "setuptools<81" wheel numpy
# CPU-only torch: the CUDA stack no longer fits the runner's disk
# quota. --no-build-isolation then reuses it for the build instead
# of resolving a second copy. See tests/smoke_tests/Dockerfile.
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation --no-cache-dir
python setup.py build_ext --inplace --force

- name: Test simulator performance
Expand Down
68 changes: 68 additions & 0 deletions .github/workflows/fork-gates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Fork-only. Never cherry-pick this file upstream.
#
# Runs the mechanical merge gates so nobody has to run them by hand or paste
# their output into a pull request. Gate 1 is the rest of CI; this job is
# check-commit plus Gate 2, reported like any other check. Text output, not
# annotations: this log is what a reviewer reads instead of a pasted block.
name: Fork gates

on:
pull_request:
branches:
- 'integration/3.0'

permissions:
contents: read

jobs:
gates:
name: Fork gates
runs-on: ubuntu-latest
steps:
- name: Checkout the pull request head
uses: actions/checkout@v4
with:
# The merge ref would put a synthetic merge commit at HEAD, and every
# gate reasons about the real commit the branch carries.
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

# Checking out a SHA leaves HEAD detached, which `mirror` refuses: it has
# to know which branch to return you to.
- name: Put HEAD on a branch
run: git switch -c gate-head

- name: Fetch upstream
run: |
git remote add upstream https://github.com/Emerge-Lab/PufferDrive.git
git fetch --no-tags --quiet upstream 3.0:refs/remotes/upstream/3.0

- name: Identify the change
id: id
run: |
branch='${{ github.event.pull_request.head.ref }}'
echo "slug=${branch#*/}" >> "$GITHUB_OUTPUT"
case "$(git log -1 --format=%s)" in
'chore(fork):'*) echo "chore=true" >> "$GITHUB_OUTPUT" ;;
*) echo "chore=false" >> "$GITHUB_OUTPUT" ;;
esac

- name: Review track
run: python .github/fork/forkctl.py track

- name: check-commit
run: |
if [ '${{ steps.id.outputs.chore }}' = 'true' ]; then
python .github/fork/forkctl.py check-commit --chore
else
python .github/fork/forkctl.py check-commit
fi

# A chore(fork) commit is fork-only by definition, so it has no upstream
# form to prove. Section 4.8 exempts it.
#
# always(): an author should get both gate results from one run, not fix
# check-commit and only then discover Gate 2 is red too.
- name: 'Gate 2: upstream-readiness'
if: always() && steps.id.outputs.chore == 'false'
run: python .github/fork/forkctl.py mirror '${{ steps.id.outputs.slug }}' --sha HEAD --no-publish
133 changes: 133 additions & 0 deletions .github/workflows/fork-only-view.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Fork-only view

# Fork-only. Verifies that the `fork-only/3.0` view branch still matches the single
# `chore(fork):` commit on `integration/3.0`, and that its standing draft PR is intact.
# It detects drift; it cannot publish, because GITHUB_TOKEN may not push workflow files.
# A stack that is behind upstream is reported by sync-mirror.yml, not here.
#
# Why it never publishes, and what it does and does not prove about tampering:
# .github/fork/PROCESS.md -> The standing draft PRs.
#
# Never cherry-pick this workflow upstream.

on:
push:
branches:
- 'integration/3.0'
- 'fork-only/3.0'
schedule:
- cron: '41 5 * * *'
workflow_dispatch:
inputs:
stack:
description: 'Stack ref to analyse'
required: false
default: 'origin/integration/3.0'
mirror:
description: 'Mirror ref to analyse'
required: false
default: 'origin/3.0'

permissions:
contents: read
pull-requests: read

concurrency:
group: fork-only-view
cancel-in-progress: false

jobs:
verify:
# The name itself carries the verdict's authority: a manual dispatch runs
# candidate detector code and must never read as the production check in
# the Actions list, where often only the job name and a green tick are seen.
name: >-
${{ github.event_name == 'workflow_dispatch'
&& 'ADVISORY -- candidate detector, not authoritative'
|| 'Check fork-only/3.0 is current' }}
runs-on: ubuntu-latest
steps:
# The detector must not come from the ref it polices. A push to
# `fork-only/3.0` is exactly the tampering this job exists to catch, so the
# code is taken from `integration/3.0` -- a protected branch -- and only a
# human-driven `workflow_dispatch` may test a candidate ref instead.
- name: Checkout the detector
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && github.ref || 'integration/3.0' }}
fetch-depth: 0

- name: Fetch the refs under analysis
run: |
git fetch --no-tags origin \
'+refs/heads/3.0:refs/remotes/origin/3.0' \
'+refs/heads/integration/3.0:refs/remotes/origin/integration/3.0'

# On push and schedule the production refs are hardcoded, so a dispatch
# input can never make the authoritative verdict describe something else.
- name: Resolve the refs under analysis
id: refs
env:
EVENT: ${{ github.event_name }}
STACK_INPUT: ${{ inputs.stack }}
MIRROR_INPUT: ${{ inputs.mirror }}
run: |
if [ "$EVENT" = "workflow_dispatch" ]; then
echo "stack=${STACK_INPUT:-origin/integration/3.0}" >> "$GITHUB_OUTPUT"
echo "mirror=${MIRROR_INPUT:-origin/3.0}" >> "$GITHUB_OUTPUT"
else
echo "stack=origin/integration/3.0" >> "$GITHUB_OUTPUT"
echo "mirror=origin/3.0" >> "$GITHUB_OUTPUT"
fi

- name: Report what is being checked
env:
EVENT: ${{ github.event_name }}
STACK: ${{ steps.refs.outputs.stack }}
MIRROR: ${{ steps.refs.outputs.mirror }}
DETECTOR: ${{ github.event_name == 'workflow_dispatch' && github.ref || 'integration/3.0' }}
run: |
if [ "$EVENT" = "workflow_dispatch" ]; then
AUTHORITY="advisory (candidate detector)"
else
AUTHORITY="authoritative"
fi
{
echo "### Fork-only view check"
echo ""
echo "| what | ref |"
echo "| --- | --- |"
echo "| authority | $AUTHORITY |"
echo "| detector code from | \`$DETECTOR\` |"
echo "| stack analysed | \`$STACK\` |"
echo "| mirror analysed | \`$MIRROR\` |"
} >> "$GITHUB_STEP_SUMMARY"
if [ "$EVENT" = "workflow_dispatch" ]; then
{
echo ""
echo "> **This run is advisory.** A dispatch executes the detector from"
echo "> \`$DETECTOR\`, which is candidate code, and may analyse non-production"
echo "> refs. Only \`push\` and \`schedule\` runs are authoritative."
} >> "$GITHUB_STEP_SUMMARY"
fi

- name: Check the stack, the view branch and the standing PR
env:
GH_TOKEN: ${{ github.token }}
STACK: ${{ steps.refs.outputs.stack }}
MIRROR: ${{ steps.refs.outputs.mirror }}
run: |
python .github/fork/forkctl.py check-view \
--stack "$STACK" --mirror "$MIRROR" --format github

self-test:
name: forkctl self-test (candidate ref)
runs-on: ubuntu-latest
steps:
# Advisory only: this deliberately runs the *candidate* code, so it proves
# the tool is sound, never that the repository state is.
- name: Checkout the triggering ref
uses: actions/checkout@v4

- name: Run the forkctl unit suite
run: python .github/fork/test_forkctl.py
16 changes: 14 additions & 2 deletions .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Installation Test

on:
pull_request:
branches: [ main, '3.0' ]
branches: [ main, '3.0', 'integration/3.0' ]
push:
branches: [ main, '3.0' ]

Expand Down Expand Up @@ -38,7 +38,19 @@ jobs:
sudo apt-get clean
sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/*

- name: Install pufferlib
- name: Install pufferlib (Linux)
if: runner.os == 'Linux'
run: |
python -m pip install -U pip "setuptools<81" wheel numpy
# CPU-only torch. The CUDA stack is ~3 GB of wheels and no longer
# fits the runner's disk quota; see the Install steps in ci.yml.
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation

- name: Install pufferlib (macOS)
if: runner.os != 'Linux'
run: |
python -m pip install -U pip
# Deliberately a plain install: macOS wheels are already CPU-only, so
# this keeps the default PEP 517 build-isolation path under test.
pip install -e .
Loading
Loading