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
973 changes: 973 additions & 0 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!-- Fork PR into integration/3.0. Delete nothing; fill everything in. -->

## What

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

## Why

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

## How tested

<!-- Commands you actually ran in this environment, and their outcome. -->

- [ ] `pre-commit run --all-files`
- [ ] `make rebuild` (required if `.c`/`.h` changed — zero new warnings)
- [ ] `make test-unit` / `make test-eval`
- [ ] `make test-c` (C sim logic)
- [ ] `make test-notebooks` (notebooks touched)
- [ ] `make test-docker-smoke` (dynamics, rewards, observations, or training loop touched)
- [ ] `make -C tests/drive perf` (hot paths touched)
- [ ] `make test` before marking ready (unit + eval + c + notebooks — does **not** include
docker-smoke or perf; those are the two boxes above)

New behaviour is covered by a new test / the fix has a test that fails without it:

<!-- name the test(s) -->

## Stack hygiene

<!-- chore(fork) branches: the two fork-only checks below are expected to trip by design —
write "exempt: chore(fork)" for those. The one-commit check still applies in full. -->

- [ ] exactly one commit (`git rev-list --count origin/integration/3.0..HEAD` → `1`)
- [ ] Conventional Commits subject, rebased on current `integration/3.0`
- [ ] no fork-only **file** in the diff (`git diff --name-only origin/integration/3.0...HEAD`)
- [ ] no fork-only **hunk** in a shared file — `AGENTS.md`, `CLAUDE.md`, and the `pull_request`
branch filters of `ci.yml` / `install.yml` are untouched in those specific places

## Merge gate

<!-- All three must be green before this is stacked. See copilot-instructions.md §4.8. -->

- [ ] **Gate 1 — CI green** on the final commit (no "green except a flake")
- [ ] **Gate 2 — adversarial review** posted by someone other than the author; findings fixed or
explicitly accepted in the thread
- [ ] **Gate 3 — upstream-readiness proven**: `mirror/<slug>` cherry-picked onto pure
`upstream/3.0`, applied without conflict, diff is upstream-clean
<!-- exempt for chore(fork): fork-only changes — tick the "Never upstream" box below -->

Gate 3 result:

<!-- paste `git diff --name-only upstream/3.0...HEAD`, or write "exempt: chore(fork)" -->

## Upstream disposition

- [ ] Mirror PR to `Emerge-Lab/PufferDrive` requested now — needs a human to verify the final
title, body and diff first, and the body must carry the authorship disclosure from
copilot-instructions.md §6.1
- [ ] Contributable, but we hold it for now, because: <!-- reason -->
- [ ] **Never upstream** — fork-only `chore(fork):` change, Gate 3 exempt

<!--
Fork-only change? Two independent axes, BOTH must be marked:

do-not-mirror (never reaches upstream) — all four:
1. PR is a DRAFT
2. title contains [do-not-mirror]
3. the `do-not-mirror` label is applied
4. the "Never upstream" box above is ticked

do-not-merge (the merge button is never pressed) — both:
5. title contains [do-not-merge]
6. the `do-not-merge` label is applied

Merging it would put a SECOND chore(fork) commit on the stack. Section 7 asserts there is exactly
one, so every future sync would abort. It is folded into the base chore(fork) commit by fixup.
See .github/copilot-instructions.md section 3.3.
-->


## Risk / notes

<!-- Behaviour changes, config or observation-layout changes, perf impact, follow-ups. -->
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
114 changes: 114 additions & 0 deletions .github/workflows/fork-only-view.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Fork-only view

# Fork-only. Republishes the single `chore(fork):` commit as `fork-only/3.0`,
# so a standing draft PR into `3.0` shows every fork-only change and nothing
# else. The branch is a derived view and is force-pushed; never commit to it.
# Never cherry-pick this workflow upstream.

on:
push:
branches: [ 'integration/3.0' ]
workflow_dispatch:

permissions:
contents: write
pull-requests: read

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

jobs:
refresh:
name: Republish fork-only/3.0
runs-on: ubuntu-latest
steps:
- name: Checkout integration stack
uses: actions/checkout@v4
with:
ref: 'integration/3.0'
fetch-depth: 0

- name: Resolve and verify the fork base commit
id: base
run: |
git fetch --no-tags origin '+refs/heads/3.0:refs/remotes/origin/3.0'
mirror=$(git rev-parse refs/remotes/origin/3.0)

if ! git merge-base --is-ancestor "$mirror" HEAD; then
echo "::error::integration/3.0 does not contain the 3.0 mirror tip; rebase the stack."
exit 1
fi

if git rev-list --min-parents=2 "$mirror..HEAD" | grep -q .; then
echo "::error::merge commit on the stack; integration/3.0 must stay linear."
exit 1
fi

if git log --format=%s "$mirror..HEAD" | grep -q '^fixup!'; then
echo "::error::stray fixup! commit on the stack."
exit 1
fi

count=$(git log --format=%s "$mirror..HEAD" | grep -c '^chore(fork): ' || true)
if [ "$count" != "1" ]; then
echo "::error::expected exactly 1 chore(fork): commit on the stack, found $count."
exit 1
fi

base=$(git rev-list "$mirror..HEAD" | tail -1)
if [ -z "$base" ]; then
echo "::error::integration/3.0 carries no commits over 3.0."
exit 1
fi

if ! git log -1 --format=%s "$base" | grep -q '^chore(fork): '; then
echo "::error::oldest stack commit $base is not the fork base."
exit 1
fi

# "<sha> <parent>" is exactly two words; a root or merge commit is not.
if [ "$(git rev-list --parents -n 1 "$base" | wc -w)" != "2" ]; then
echo "::error::fork base $base does not have exactly one parent."
exit 1
fi

if [ "$(git rev-parse "$base^")" != "$mirror" ]; then
echo "::error::fork base $base is not parented on the 3.0 mirror tip $mirror."
exit 1
fi

echo "base=$base" >> "$GITHUB_OUTPUT"

- name: Force-push the view branch
env:
BASE: ${{ steps.base.outputs.base }}
run: |
git push --force origin "$BASE:refs/heads/fork-only/3.0"
{
echo "### Fork-only view refreshed"
echo "\`fork-only/3.0\` -> \`$BASE\`"
} >> "$GITHUB_STEP_SUMMARY"

- name: Check the standing view PR
env:
GH_TOKEN: ${{ github.token }}
run: |
merged=$(gh pr list --head fork-only/3.0 --base '3.0' --state merged --json number --jq 'length')
if [ "$merged" != "0" ]; then
echo "::error::a fork-only/3.0 PR was MERGED into the 3.0 mirror — the mirror is contaminated."
exit 1
fi

open=$(gh pr list --head fork-only/3.0 --base '3.0' --state open --json number --jq 'length')
if [ "$open" -gt 1 ]; then
echo "::error::$open open fork-only/3.0 PRs; exactly one standing PR is expected."
exit 1
fi

if [ "$open" = "0" ]; then
echo "::warning::no standing fork-only/3.0 PR is open. A human opens it once — see section 2."
exit 0
fi

echo "Standing view PR #$(gh pr list --head fork-only/3.0 --base '3.0' --state open --json number --jq '.[0].number') is open."
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