Skip to content

fix(ci): install CPU-only torch wheels in GitHub Actions - #3

Merged
jdsika merged 1 commit into
integration/3.0from
feature/ci-cpu-torch-wheels
Oct 23, 2026
Merged

jdsika merged 1 commit into
integration/3.0from
feature/ci-cpu-torch-wheels

Conversation

@jdsika

@jdsika jdsika commented Oct 23, 2026 •

Copy link
Copy Markdown

What

Install CPU-only torch wheels in the Linux GitHub Actions jobs, and build with
--no-build-isolation so the build reuses them.

Why

Every Linux job fails while installing the package:

[Errno 122] Disk quota exceeded

Errno 122 is EDQUOT, raised while unpacking the CUDA torch stack that
pip install -e . resolves by default: torch is 554.6 MB and the nvidia-*
wheels add roughly 2.4 GB more. The failure lands during "Installing build
dependencies"
- the isolated PEP 517 build environment is enough to exceed
the quota on its own, before the runtime install is even attempted.

This is a runner image change, not a code change

upstream 11_bernhard_dev this PR's base
date 2026-10-20 2026-10-23
Image: ubuntu-24.04 ubuntu-26.04
torch wheel 2.14.1, 554.6 MB 2.14.1, 554.6 MB
result success failure

Same workflows, same resolved wheel set, three days apart. The ubuntu-latest
label began migrating to Ubuntu 26 on 2026-10-19,
which falls between those two runs. (That issue is cited for the rollout date
only - it documents the migration, not a quota change.)

Both workflows already carry Free up disk space steps, so these jobs were
running close to the limit well before the migration. Nothing in the repo
changed; the headroom did.

How

python -m pip install -U pip "setuptools<81" wheel numpy
pip install --index-url https://download.pytorch.org/whl/cpu torch
pip install -e . --no-build-isolation
  • The CPU index is the decisive part - it keeps the stack small enough to fit.
  • --no-build-isolation stops pip from resolving torch a second time for the
    build environment (which would pull CUDA wheels from PyPI regardless of what
    the runtime environment already has). It requires build deps up front, hence
    the explicit setuptools/wheel/numpy line. The setuptools<81 bound
    mirrors the pin already present in install_requires.
  • tests/smoke_tests/Dockerfile already installs torch exactly this way, so
    this aligns CI with the container the smoke tests run in.

Coverage

No GPU coverage is lost. setup.py gates the CUDA extension on
BUILD_CUDA_EXT, which keys off CUDA_HOME/ROCM_HOME - i.e. presence of a
CUDA toolkit, not a GPU and not the torch wheel variant. Neither is set on
these runners, so the CUDA sources were never compiled in CI. Verified by
diffing a CUDA-torch build log against this one: no nvcc, no -ltorch_cuda
in either.

macOS is deliberately left on the plain pip install -e .. Its PyPI wheels
are already CPU-only, so converting it would be a no-op on disk usage while
removing the last job that exercises the default PEP 517 build-isolation path.
The macOS install behaviour in this PR is byte-for-byte the pre-existing one.

The one real trade-off: the Linux half of install.yml no longer exercises
build isolation. That is the cost of the fix, and the macOS job is what keeps
the path covered.

Known limitation

--index-url replaces PyPI rather than supplementing it, so a handful of
non-torch dependencies are now served from download.pytorch.org (it mirrors
them). If a future dependency ever constrains torch, pip could resolve a
CUDA build from PyPI and the quota failure would return. It would fail loudly
and identically, but the cause would be non-obvious - worth a pin or an
--extra-index-url rework if that ever bites.

Testing

All 12 checks green on this branch (Unit / C Drive / Eval / Notebooks / Smoke
Performance on Linux, install matrix on Linux + macOS, Docker smoke 14m39s).

Follow-ups (not in this PR)

  • [build-system] requires still pins numpy<2.0 while the runtime resolves
    numpy 2.x and the extension is rebuilt against 2.x headers. The pin is stale
    and misleading; worth a separate change.
  • Note that torch cannot simply be dropped from build-system.requires -
    setup.py imports torch.utils.cpp_extension at module scope.

@jdsika

jdsika commented Oct 23, 2026

Copy link
Copy Markdown
Author

Gate 3 — upstream-readiness: PASSED

Verified by actually building the mirror branch against pure upstream, per copilot-instructions.md section 4.8.

$ git fetch upstream 3.0
$ git switch -c mirror/ci-cpu-torch-wheels upstream/3.0
$ git cherry-pick 8b70263d
[mirror/ci-cpu-torch-wheels a78955e8] fix(ci): install CPU-only torch wheels in GitHub Actions
 2 files changed, 31 insertions(+), 10 deletions(-)

Clean cherry-pick onto upstream/3.0 (74b44c2f) — no conflict.

Upstream-cleanliness checks:

$ git diff --name-only upstream/3.0..HEAD
.github/workflows/ci.yml
.github/workflows/install.yml

$ git grep -n "integration/3.0" -- .github/workflows/ci.yml .github/workflows/install.yml
(no matches)
Check Result
Cherry-picks onto pure upstream/3.0 without conflict pass
Diff limited to the feature's own files pass
No integration/3.0 branch-filter leak into either workflow pass
No fork-only files (copilot-instructions.md, CONTRIBUTING.md, PR template, sync-mirror.yml) pass
No fork:begin/fork:end block in AGENTS.md / CLAUDE.md pass

This commit is contributable to Emerge-Lab/PufferDrive as-is.

Note on the stack-hygiene check

This PR is the case that exposed a flaw in the old instructions. git diff --name-only lists .github/workflows/ci.yml and .github/workflows/install.yml, and the old section 3 listed those as "fork-only paths" — so a literal reading said this PR was contaminated and unmergeable.

It is not. Only the 'integration/3.0' entry in those files' pull_request branch filter is fork-only, and this commit does not touch it — confirmed by the git grep above returning nothing on the mirror branch. The instructions have been split into fork-only files (file-name check) and fork-only hunks in shared files (content check) in #4.

Gate status

  • Gate 1 — CI green — all 12 checks passed
  • Gate 2 — adversarial review — in progress
  • Gate 3 — upstream-readiness proven — above

Not merging until Gate 2 lands.

The Linux CI jobs fail while installing the package:

    [Errno 122] Disk quota exceeded

Errno 122 is EDQUOT, hit while unpacking the CUDA torch stack that
`pip install -e .` resolves by default. torch alone is 554.6 MB and the
accompanying nvidia-* wheels add another ~2.4 GB, which no longer fits
the runner's quota. The failure occurs during "Installing build
dependencies", i.e. the isolated PEP 517 build environment is already
enough to exceed it on its own.

The trigger is a runner image change, not a code change:

  * upstream 11_bernhard_dev, 2026-10-20, Image: ubuntu-24.04 -> success
  * same workflows, 2026-10-23, Image: ubuntu-26.04 -> failure

Both resolve an identical wheel set (torch 2.14.1, 554.6 MB). The
`ubuntu-latest` label began migrating to Ubuntu 26 on 2026-10-19
(actions/runner-images#14748), between those two runs. Both workflows
already carry "Free up disk space" steps, so these jobs were running
close to the limit before the migration.

Fix: install CPU-only torch from download.pytorch.org, then build with
--no-build-isolation so the build reuses it instead of resolving torch
a second time. --no-build-isolation needs the build dependencies present
up front, hence the explicit setuptools/wheel/numpy install; the
`setuptools<81` bound matches the pin already in install_requires.

This loses no coverage: BUILD_CUDA_EXT keys off CUDA_HOME/ROCM_HOME,
which is unset on these runners, so the CUDA sources were never compiled
here. tests/smoke_tests/Dockerfile already installs torch this way.

macOS keeps the plain `pip install -e .` unchanged: its PyPI wheels are
already CPU-only, and leaving it alone keeps the default PEP 517
build-isolation path under test somewhere in the matrix.
@jdsika
jdsika force-pushed the feature/ci-cpu-torch-wheels branch from 8b70263 to 652e080 Compare October 23, 2026 10:29
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
@jdsika

jdsika commented Oct 23, 2026

Copy link
Copy Markdown
Author

Gate 2 — adversarial review record

Reviewed in a separate task with no authoring context, per copilot-instructions.md §4.7. The code
change was found sound; the explanation attached to it was not. Four factual defects in the
commit message / PR body were found and fixed, and one real design flaw in the change itself.

Findings fixed

# Finding Resolution
1 Root cause was stated as "CUDA wheels installed twice". The log shows the failure during Installing build dependencies — install #1 alone exhausts the quota. Size is the decisive variable, not duplication. Narrative rewritten. The CPU index is now described as the decisive fix and --no-build-isolation as what stops a second resolution.
2 [Errno 122] is EDQUOT (quota), not ENOSPC. "26.04 has less disk" was an inference, and actions/runner-images#14748 documents only the rollout date, not any quota change. Body now says EDQUOT and cites #14748 for the date only.
3 "Upstream has not pushed since 2026-10-05" was false. Verified and corrected: upstream ran green on 2026-10-20 on ubuntu-24.04 with a byte-identical wheel set. This strengthens the thesis — the gap narrows to 3 days with only the image differing.
4 Suggested follow-up "drop torch from build-system.requires" would break the build — setup.py:11 does from torch.utils import cpp_extension at module scope. Deleted, and replaced with an explicit warning that torch cannot be removed.
5 Design flaw: converting macOS was gratuitous. macOS was already passing, its PyPI wheels are already CPU-only, and converting it removed the last job exercising the default PEP 517 build-isolation path. macOS reverted to plain pip install -e . — now byte-for-byte upstream's original. Both macOS jobs green (2m46s / 1m46s).

Findings accepted, not fixed

  • install.yml's Linux half no longer tests the plain install path the workflow is named after.
    Accepted as the unavoidable cost; documented in the body, and macOS now keeps that path covered.
  • --index-url replaces PyPI, so some non-torch deps resolve from download.pytorch.org. If a
    future dep constrains torch, CUDA wheels could return and the failure would look novel.
    Documented as a Known limitation.

Probes that found nothing

  • numpy ABI mismatch — not a risk. ci.yml already re-runs build_ext --inplace --force in the
    runtime env, so artifacts were built against numpy 2.x headers even before this change
    (numpy/_core/include appears in upstream's pre-change log). import_array() in PyInit_binding
    makes any real mismatch loud, not silent.
  • setuptools<81 — justified; it mirrors an existing pin in install_requires.
  • CUDA extension coverage — confirmed never compiled in CI. Note the PR's original reasoning
    ("CI never had a GPU") was the wrong mechanism for a correct conclusion: BUILD_CUDA_EXT keys off
    CUDA_HOME/ROCM_HOME, i.e. toolkit presence, not GPU presence or wheel variant. Body corrected.
  • Determinism / test coverage — no test behaviour changed; this is CI plumbing only.

Gate status

  • Gate 1 — green. All 12 checks pass on 652e0806, including Docker smoke (14m32s).
  • Gate 2 — passed, findings above fixed or explicitly accepted.
  • Gate 3 — passed. Clean cherry-pick onto pure upstream/3.0; diff contains only the two
    workflow files, no fork-only files, no integration/3.0 filter leak.

Reviewer's verdict was merge into the stack: yes; send upstream as-is: no — the "no" referred
entirely to the defective narrative, which has now been rewritten. A human should read the final
body before it is offered upstream.

jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
@jdsika

jdsika commented Oct 23, 2026

Copy link
Copy Markdown
Author

Gate 3 — re-verified against the amended commit

The commit was amended after review (macOS reverted to the plain install, narrative rewritten), so
the previous Gate 3 proof was stale. Rebuilt from scratch against current upstream/3.0
(74b44c2f) using the §6 recipe:

base: 74b44c2f  →  cherry-pick 652e0806  →  8458cb43
commits vs upstream/3.0 ............ 1
files in mirror diff ............... .github/workflows/ci.yml
                                     .github/workflows/install.yml
fork-only FILE leak (§3.1) ......... none
fork-only HUNK leak (§3.2) ......... none   (no `integration/3.0`, no `fork:begin`)

Cherry-pick applied with a clean auto-merge, no conflict. Gate 3 passes — the commit is
contributable to Emerge-Lab/PufferDrive as-is.

Byproduct: this run found a bug in our own docs

Executing the recipe rather than reading it exposed a defect in the §6 text: it starts with
git switch --detach upstream/3.0 (so the branch can be deleted even when checked out), which makes
the documented git switch - return step fail with:

fatal: a branch is expected, got commit '74b44c2f...'

Fixed in #4 — both §4.8 and §6 now name the target branch explicitly instead of relying on -.

Gate summary

Gate Status
1 — CI green ✅ all 12 checks on 652e0806, incl. Smoke Train 14m32s and both macOS jobs
2 — adversarial review ✅ findings posted above; 5 fixed, 2 explicitly accepted
3 — upstream-readiness ✅ proven above

Ready for a human to press Rebase and merge. Per §5 an agent does not merge into
integration/3.0 on its own initiative.

Note the ordering dependency: #4 is currently red on every Linux job with this exact
[Errno 122] failure, because it is based on integration/3.0, which does not yet contain this
fix. #4 touches only three markdown files and cannot have caused it. This PR has to land first —
which is precisely what §4.8 Gate 1 prescribes for an unrelated broken check.

jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
@jdsika

jdsika commented Oct 23, 2026

Copy link
Copy Markdown
Author

Mirror PR opened upstream: Emerge-Lab#602

ASCS-eV:mirror/ci-cpu-torch-wheels → Emerge-Lab:3.0, +37/-9 across 2 files. Human-approved title, body and diff per §5.

Gate 3 re-verified against current upstream/3.0 (74b44c2f3) immediately before pushing: mirror parent is an ancestor, no fork-only file, no fork-only hunk.

This PR (#3) can now be stacked. Tracking no longer depends on it staying open — per §6.2 the mirror PR is the long-lived artifact, and it is independent of this one by construction. When this merges, GitHub closes it and deletes feature/ci-cpu-torch-wheels; Emerge-Lab#602 is unaffected.

Lifecycle from here (§6.2 → §6.3):

Event This PR Emerge-Lab#602 Stack
Human presses Rebase and merge merged, closed open +1 (feature gap 1)
Upstream merges Emerge-Lab#602 — closed by upstream unchanged
Next sync + rebase — — −1, drops as empty (feature gap 0)

Upstream checks are action_required — GitHub holds fork PRs from first-time contributors until a maintainer presses Approve and run workflows. Not a defect. Once approved, the runs use the workflow files from the merge commit, so the proposed fix is exercised by the checks that gate it.

Framing note: upstream's CI is currently green (last run 2026-10-20 on ubuntu-24.04). Ours fails only because we drew ubuntu-26.04. Emerge-Lab#602 is written as preventive, not as a live-outage report.

@jdsika
jdsika merged commit 7ae6c83 into integration/3.0 Oct 23, 2026
12 checks passed
@jdsika
jdsika deleted the feature/ci-cpu-torch-wheels branch October 23, 2026 12:03
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3): a
draft PR titled `[do-not-mirror]`, carrying the `do-not-mirror` label, with
the template's "Never upstream" box ticked. Four redundant markers, on
purpose. It is folded into the base commit by fixup rather than merged, so
the stack keeps exactly one fork commit.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
jdsika added a commit that referenced this pull request Oct 23, 2026
`integration/3.0` is both what BMW pins and the base every later feature
rebases onto, so a defect merged there is multiplied across every consumer
and every subsequent PR. Opening a PR was previously enough to get a change
onto the stack; now it is not.

A feature may be stacked only after three gates:

  1. CI green on the final commit.
  2. An adversarial review by someone other than the author. Section 4.7
     defines what that means concretely -- revert the fix and prove the new
     test fails, hunt edge cases, determinism, trust boundaries, observation
     and config layout, hot-path cost, scope creep, and likely upstream
     objections -- and requires the findings to be written down, including
     the probes that found nothing.
  3. Upstream-readiness proven rather than asserted: the commit is
     cherry-picked onto a branch cut from pure `upstream/3.0` *before*
     merging, and must apply without conflict and produce an upstream-clean
     diff. We stack only commits we could contribute today.

`chore(fork):` commits are exempt from gate 3 -- fork-only infrastructure
can never be upstream-ready -- and gain their own flow (section 3.3). Two
orthogonal axes must both be marked, because they fail in different
directions: `do-not-mirror` (must never reach upstream; marked four times
over -- title prefix, label, `chore(fork):` commit type, and the template's
"Never upstream" box) and `do-not-merge` (the merge button must never be
pressed on it; marked by title prefix and label). Merging such a PR would
put a *second* `chore(fork):` commit on the stack, and section 7 asserts
there is exactly one, so every future sync would abort until someone
unpicked it by hand. It is folded into the base commit by fixup instead, so
the stack keeps exactly one fork commit and the PR ends closed, not merged.

Also fixes a real ambiguity in the old text. Section 3 listed fork-only
*files* and fork-only *hunks inside shared files* as one flat list, and
section 4.5 then checked them with a file-name diff. That check cannot see
hunks, and it falsely flags any PR that legitimately edits a shared file --
which is exactly what the CI fix in #3 does to `ci.yml` and `install.yml`.
Split into 3.1 (whole files) and 3.2 (hunks in shared files), each with a
check that actually detects it.

Smaller changes:
- the default branch *is* the process. A clone checks out `integration/3.0`,
  so a rule that lives only in an open PR has no effect on any agent or on
  the BMW submodule consumers. Section 2 states this, 4.1 now opens by
  listing in-flight process PRs (the two labels AND-ed, which excludes the
  standing draft PR), and 3.3 requires the fold-in window to be kept short
  and the output of agents that ran during it to be re-checked.
- document `chore/<slug>` branch naming, previously undefined
- agents may not review their own work, nor merge to `integration/3.0` on
  their own initiative
- section 6 no longer rebuilds the mirror branch from scratch, since gate 3
  already built and verified it, and now requires re-running the suites on
  the mirror branch because it has a different base than CI tested
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant