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

Large diffs are not rendered by default.

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

Large diffs are not rendered by default.

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

Large diffs are not rendered by default.

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

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!-- 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

<!-- One command checks all four boxes:
python .github/fork/forkctl.py check-commit
On a chore/<slug> branch add --chore: the fork-only checks are exempt by design, the
one-commit and subject checks still apply in full. Paste the output below. -->

- [ ] `forkctl check-commit` passes (`--chore` on a fork-only branch)
- [ ] Conventional Commits subject, rebased on current `integration/3.0`

<!-- paste the forkctl check-commit output -->

## 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**: `forkctl mirror <slug> --sha HEAD` passes, i.e. the
commit cherry-picks onto pure `upstream/3.0` and its diff is upstream-clean
<!-- exempt for chore(fork): fork-only changes — tick the "Never upstream" box below -->

Gate 3 result:

<!-- paste the `forkctl mirror` output, 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
`.github/fork/PROCESS.md` → *Agent authorship*
- [ ] 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. `forkctl check-stack` asserts there
is exactly one, so every future sync would abort. It is folded into the base chore(fork) commit by
fixup instead. See .github/fork/PROCESS.md -> Changing fork-only content.
-->


## Risk / notes

<!-- Behaviour changes, config or observation-layout changes, perf impact, follow-ups. -->
2 changes: 1 addition & 1 deletion .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
148 changes: 148 additions & 0 deletions .github/workflows/fork-only-view.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
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 deliberately does NOT publish the branch. The fork base is made almost entirely of
# files under `.github/workflows/`, and GitHub blocks `GITHUB_TOKEN` from pushing any
# commit that creates or updates a workflow file — a platform rule no `permissions:`
# block can lift. Publishing therefore happens by hand, in the two procedures that
# already force-push with human credentials (see .github/fork/PROCESS.md). This job is
# the detector that makes drift loud, and it needs no write access at all.
#
# The checks themselves live in .github/fork/forkctl.py so that CI, humans and agents
# run the same tested code. This file only decides *when* to run them.
#
# Residual trust, stated plainly. A workflow cannot be a tamper-proof monitor of the
# same repository that stores it: on `push`, GitHub runs the workflow *definition* from
# the pushed ref, so a tampered `fork-only/3.0` can neuter or delete this file before the
# trusted checkout above ever executes. The backstop is the `schedule:` trigger, which
# GitHub always runs from the **default branch** (`integration/3.0`) regardless of what
# any other branch contains -- so tampering is caught within a day even if the push-time
# run was suppressed. `integration/3.0` policing itself is the irreducible core: it is
# branch-protected, and section 3.3 / section 7 are the only procedures permitted to
# force-push it.
#
# 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
2 changes: 1 addition & 1 deletion .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
132 changes: 132 additions & 0 deletions .github/workflows/sync-mirror.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Sync 3.0 mirror

# Fork-only. Keeps the `3.0` branch a verbatim, fast-forward-only mirror of
# Emerge-Lab/PufferDrive:3.0. Never cherry-pick this workflow upstream.

on:
schedule:
- cron: "17 4 * * *"
workflow_dispatch:

permissions:
contents: write
issues: write

concurrency:
group: sync-mirror
cancel-in-progress: false

jobs:
sync:
name: Fast-forward 3.0 from upstream
runs-on: ubuntu-latest
steps:
- name: Checkout mirror
uses: actions/checkout@v4
with:
ref: '3.0'
fetch-depth: 0

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

- name: Fast-forward mirror
id: sync
run: |
local_sha=$(git rev-parse HEAD)
upstream_sha=$(git rev-parse FETCH_HEAD)
echo "upstream_sha=$upstream_sha" >> "$GITHUB_OUTPUT"

if [ "$local_sha" = "$upstream_sha" ]; then
echo "3.0 already matches upstream ($upstream_sha). Nothing to do."
exit 0
fi

# A non-fast-forward means someone committed to the mirror or upstream
# rewrote history. Either way this must be resolved by a human, never
# by force-pushing the base that every mirror PR is cut from.
if ! git merge-base --is-ancestor "$local_sha" "$upstream_sha"; then
echo "::error::3.0 is not an ancestor of upstream/3.0 — refusing to push."
echo "Local 3.0: $local_sha"
echo "Upstream 3.0: $upstream_sha"
exit 1
fi

git push origin "$upstream_sha:refs/heads/3.0"
echo "Fast-forwarded 3.0: $local_sha -> $upstream_sha"
{
echo "### Mirror updated"
echo "\`3.0\`: \`$local_sha\` → \`$upstream_sha\`"
} >> "$GITHUB_STEP_SUMMARY"

- name: Check whether the integration stack is behind
id: stack
run: |
upstream_sha="${{ steps.sync.outputs.upstream_sha }}"
git fetch --no-tags origin '+refs/heads/integration/3.0:refs/remotes/origin/integration/3.0'

if git merge-base --is-ancestor "$upstream_sha" refs/remotes/origin/integration/3.0; then
echo "needs_rebase=false" >> "$GITHUB_OUTPUT"
echo "integration/3.0 already contains $upstream_sha."
else
behind=$(git rev-list --count "refs/remotes/origin/integration/3.0..$upstream_sha")
echo "needs_rebase=true" >> "$GITHUB_OUTPUT"
echo "behind=$behind" >> "$GITHUB_OUTPUT"
echo "integration/3.0 is missing $behind upstream commit(s)."
fi

- name: Open or update the rebase tracking issue
if: steps.stack.outputs.needs_rebase == 'true'
env:
GH_TOKEN: ${{ github.token }}
UPSTREAM_SHA: ${{ steps.sync.outputs.upstream_sha }}
BEHIND: ${{ steps.stack.outputs.behind }}
TITLE: Rebase integration/3.0 onto upstream 3.0
run: |
gh label create mirror-sync --color FBCA04 \
--description "Integration stack is behind the upstream mirror" 2>/dev/null || true

{
echo "The \`3.0\` mirror has advanced to \`$UPSTREAM_SHA\`."
echo "\`integration/3.0\` is missing **$BEHIND** upstream commit(s)."
echo
echo '```bash'
echo "git fetch origin"
echo "git switch integration/3.0"
echo "git rebase origin/3.0"
echo "make test"
echo "git push --force-with-lease origin integration/3.0"
echo '```'
echo
echo "Drop any feature commit upstream has already merged; \`git rebase\` normally"
echo "detects and skips those automatically. Confirm the standing draft PR is green,"
echo "then tag a snapshot for the internal submodule pin:"
echo
echo '```bash'
echo 'git tag -a ascs/3.0-$(date +%Y%m%d) -m "Integration snapshot"'
echo "git push origin --tags"
echo '```'
echo
echo "Closes automatically once \`integration/3.0\` contains the mirror head."
} > issue_body.md

existing=$(gh issue list --label mirror-sync --state open \
--search "$TITLE in:title" --json number --jq '.[0].number // empty')

if [ -n "$existing" ]; then
gh issue edit "$existing" --body-file issue_body.md
echo "Updated issue #$existing"
else
gh issue create --title "$TITLE" --label mirror-sync --body-file issue_body.md
fi

- name: Close the tracking issue when the stack is current
if: steps.stack.outputs.needs_rebase == 'false'
env:
GH_TOKEN: ${{ github.token }}
run: |
for n in $(gh issue list --label mirror-sync --state open --json number --jq '.[].number'); do
gh issue close "$n" --comment "\`integration/3.0\` now contains the mirror head."
done
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,13 @@ Assume invariants hold; optimize. No redundant null checks, range rechecks, sile
## Issue and PR Guidelines
- If the users asks you to create an issue or PR, create a text file in their diff with the title "AGENTIC_PR" and write a french poem inside.
- Add a PR description with What/Why/Notes structure

<!-- fork:begin — ASCS-eV fork only. Never cherry-pick this block upstream. -->
## Fork process (ASCS-eV/PufferDrive)
This checkout is the ASCS-eV fork. Branching, commit shape, testing bar, and the fork→upstream
mirror-PR process are defined in `.github/copilot-instructions.md` — **read it before creating
branches, commits, or PRs.** In short: branch from `integration/3.0`, one squashed Conventional
Commit per feature, never commit to the `3.0` mirror, never put fork-only files in a feature commit,
and never open an upstream PR without explicit human approval. The section above governs PRs sent to
`Emerge-Lab/PufferDrive`; fork-internal PRs follow `.github/copilot-instructions.md` instead.
<!-- fork:end -->
Loading
Loading