Skip to content
Merged
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
266 changes: 266 additions & 0 deletions .agents/docs/2026-07-22-contract-admission-design.md

Large diffs are not rendered by default.

113 changes: 113 additions & 0 deletions .github/actions/bootstrap-mcpp/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: bootstrap-mcpp
description: >
Restore the shared CI cache lineage (mcpp sandbox + xlings + target/) and
bootstrap a released mcpp via xlings. Exports MCPP and XLINGS_BIN.

Extracted so the split CI jobs (build / toolchain legs / e2e shards /
integration) share ONE definition instead of copy-pasting a 40-line
preamble per job. Every job that uses it lands on the same cache keys,
which is what makes splitting cheap: each job restores a warm sandbox
and only pays one incremental `mcpp build`.

inputs:
xlings-version:
description: xlings release to bootstrap from
required: false
default: '0.4.30'
cache-target:
description: also restore/save target/ (build artifacts + BMIs)
required: false
default: 'true'

runs:
using: composite
steps:
# NOTE: the "-ci-" segment keeps this lineage disjoint from release.yml's
# "-release-" caches. A bare "mcpp-sandbox-<os>-" restore prefix used to
# match the release sandbox too, silently swapping in a differently
# populated registry (issue #120).
- name: Cache mcpp sandbox
uses: actions/cache@v4
with:
path: ~/.mcpp
key: mcpp-sandbox-${{ runner.os }}-ci-${{ hashFiles('mcpp.toml', '.xlings.json') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-ci-

- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-v2-

- name: Bootstrap mcpp via xlings (unix)
if: runner.os != 'Windows'
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: ${{ inputs.xlings-version }}
run: |
# Always install the pinned version — the cache may hold an older
# xlings whose sysroot/packages are incompatible.
case "$(uname -s)" in
Darwin) tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz" ;;
*) tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" ;;
esac
WORK=$(mktemp -d)
curl -fsSL -o "${WORK}/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "${WORK}/${tarball}" -C "${WORK}"
"${WORK}/${tarball%.tar.gz}/subos/default/bin/xlings" self install
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH"
xlings --version
# xim:mcpp — `xlings install` is idempotent, so cache hits skip the
# download.
xlings install mcpp -y
MCPP="$HOME/.xlings/subos/default/bin/mcpp"
test -x "$MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"

- name: Bootstrap mcpp via xlings (windows)
if: runner.os == 'Windows'
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: ${{ inputs.xlings-version }}
run: |
WORK=$(mktemp -d)
zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip"
curl -fsSL -o "${WORK}/${zipfile}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${zipfile}"
cd "${WORK}"
unzip -q "${zipfile}"
"$WORK/xlings-${XLINGS_VERSION}-windows-x86_64/subos/default/bin/xlings.exe" self install
export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH"
echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH"
xlings.exe --version
xlings.exe install mcpp -y
MCPP=$(find "$USERPROFILE/.xlings" -name "mcpp.exe" -path "*/bin/*" 2>/dev/null | head -1)
if [ -z "$MCPP" ]; then
MCPP=$(find "$USERPROFILE/.xlings" -name "mcpp" -path "*/bin/*" 2>/dev/null | head -1)
fi
test -n "$MCPP" || { echo "FAIL: mcpp not found after xlings install"; exit 1; }
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe")" >> "$GITHUB_ENV"

# Precise key on src/ + manifest so a no-source-change run lands on a full
# hit; layered restore-keys let partial hits keep BMI/dyndep state for a
# proper incremental build.
- name: Cache target/ (build artifacts + BMIs)
if: inputs.cache-target == 'true'
uses: actions/cache@v4
with:
path: target
key: mcpp-target-${{ runner.os }}-${{ github.job }}-${{ hashFiles('src/**', 'tests/**', 'mcpp.toml', 'mcpp.lock') }}
restore-keys: |
mcpp-target-${{ runner.os }}-${{ github.job }}-
mcpp-target-${{ runner.os }}-
108 changes: 108 additions & 0 deletions .github/actions/setup-macos-llvm/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: setup-macos-llvm
description: >
macOS ARM64 setup shared by ci-macos.yml and ci-macos-e2e.yml: xlings +
the xlings-managed LLVM + a dev mcpp sandbox wired to that exact LLVM.
Exports LLVM_ROOT, CXX, MCPP_LLVM_VER, MCPP, XLINGS_BIN.

Kept separate from bootstrap-mcpp: macOS does not reuse the ~/.mcpp cache
lineage — it builds its sandbox from the freshly resolved LLVM package on
every run, which is exactly the property the macOS job exists to prove.

inputs:
xlings-version:
description: xlings release to bootstrap from
required: false
default: '0.4.30'

runs:
using: composite
steps:
- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-macos15-arm64-v3-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-macos15-arm64-v3-

- name: Bootstrap xlings
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: ${{ inputs.xlings-version }}
run: |
WORK=$(mktemp -d)
tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz"
curl -fsSL -o "${WORK}/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "${WORK}/${tarball}" -C "${WORK}"
XLINGS_DIR="${WORK}/xlings-${XLINGS_VERSION}-macosx-arm64"
"$XLINGS_DIR/subos/default/bin/xlings" self install
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
xlings --version
echo "PATH=$HOME/.xlings/subos/default/bin:$PATH" >> "$GITHUB_ENV"

- name: Install LLVM via xlings
shell: bash
run: |
# latest-first: test binaries now link the toolchain's own libc++
# (A1 root fix in flags.cppm), so new llvm releases are
# self-consistent — the macOS job is the proof for each new default.
xlings install llvm -y || xlings install llvm@20.1.7 -y
LLVM_ROOT=$(find "$HOME/.xlings" -path "*/xpkgs/xim-x-llvm/*/bin/clang++" | head -1 | xargs dirname | xargs dirname)
ls "$LLVM_ROOT/bin/clang++"
"$LLVM_ROOT/bin/clang++" --version
echo "LLVM_ROOT=$LLVM_ROOT" >> "$GITHUB_ENV"
echo "CXX=$LLVM_ROOT/bin/clang++" >> "$GITHUB_ENV"

- name: Bootstrap mcpp via xlings
shell: bash
run: |
xlings install mcpp -y
MCPP="$HOME/.xlings/subos/default/bin/mcpp"
test -x "$MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"

- name: Configure dev mcpp sandbox to reuse xlings LLVM
shell: bash
run: |
# Use EXACTLY the LLVM the install step resolved (env LLVM_ROOT) — a
# version-glob pick chose a stale cached 20.1.7 next to the freshly
# installed 22.1.8 and silently tested the wrong toolchain.
LLVM_PKG="$LLVM_ROOT"
test -d "$LLVM_PKG"
LLVM_VER=$(basename "$LLVM_PKG")
echo "MCPP_LLVM_VER=$LLVM_VER" >> "$GITHUB_ENV"
MCPP_LLVM_LINK="$HOME/.mcpp/registry/data/xpkgs/xim-x-llvm/$LLVM_VER"
printf '1\n' > "$LLVM_PKG/.mcpp_ok"

mkdir -p "$HOME/.mcpp/registry/data/xpkgs/xim-x-llvm"
rm -rf "$MCPP_LLVM_LINK"
ln -s "$LLVM_PKG" "$MCPP_LLVM_LINK"

mkdir -p "$HOME/.mcpp"
cat > "$HOME/.mcpp/config.toml" <<EOF
# mcpp global config for CI. Dev binaries under target/.../bin/mcpp
# fall back to ~/.mcpp; keep its registry isolated while reusing
# the LLVM package already installed by \`xlings install llvm\`.
[xlings]
binary = "$HOME/.xlings/subos/default/bin/xlings"

[index]
default = "mcpplibs"

[index.repos."mcpplibs"]
url = "https://github.com/mcpp-community/mcpp-index.git"

[cache]
search_ttl_seconds = 3600

[build]
default_jobs = 0
default_backend = "ninja"
EOF

cat "$HOME/.mcpp/config.toml"
ls "$MCPP_LLVM_LINK/bin/clang++"
28 changes: 18 additions & 10 deletions .github/workflows/ci-fresh-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ concurrency:
group: ci-fresh-install
cancel-in-progress: false # use false to test in PRs, true to only test released mcpp

# Version under test. Bare `xlings install mcpp` resolves "newest in the
# runner's index copy", which is NOT the same source the wait-index job
# polls (raw.githubusercontent.com) — on 2026-07-21 the guard reported
# "index tracks 0.0.102" and the jobs still installed 0.0.100 ten seconds
# later, which then met an index whose floor was 0.0.101 (#265). Pinning
# makes the version explicit: if the index cannot serve it yet, the job
# fails with `version not found` instead of silently testing an older
# binary. Bump together with the .xlings.json workspace pin at release.
env:
MCPP_PIN: '0.0.102'

jobs:
# ──────────────────────────────────────────────────────────────────
# Linux: gcc@16.1.0, musl-gcc@16.1.0, llvm@20.1.7
Expand Down Expand Up @@ -86,10 +97,9 @@ jobs:
- name: Install mcpp and config mirror
run: |
# The release tarball bundles a pkgindex snapshot frozen at
# build time; refresh it so the workspace pin in .xlings.json
# (latest mcpp) resolves.
# build time; refresh it so the pinned mcpp resolves.
xlings update
xlings install mcpp -y -g # install to global
xlings install "mcpp@${MCPP_PIN}" -y -g # install to global
mcpp --version
mcpp self config --mirror GLOBAL

Expand Down Expand Up @@ -230,7 +240,7 @@ jobs:
run: |
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
xlings update
xlings install mcpp -y -g
xlings install "mcpp@${MCPP_PIN}" -y -g
mcpp --version
mcpp self config --mirror GLOBAL

Expand Down Expand Up @@ -283,10 +293,9 @@ jobs:

- name: Install mcpp and config mirror
run: |
# Refresh the bundled pkgindex snapshot so the workspace pin
# in .xlings.json (latest mcpp) resolves.
# Refresh the bundled pkgindex snapshot so the pinned mcpp resolves.
xlings update
xlings install mcpp -y -g # install to global
xlings install "mcpp@${MCPP_PIN}" -y -g # install to global
mcpp --version
mcpp self config --mirror GLOBAL

Expand Down Expand Up @@ -342,10 +351,9 @@ jobs:
- name: Install mcpp and config mirror
shell: pwsh
run: |
# Refresh the bundled pkgindex snapshot so the workspace pin
# in .xlings.json (latest mcpp) resolves.
# Refresh the bundled pkgindex snapshot so the pinned mcpp resolves.
xlings update
xlings install mcpp -y -g --verbose
xlings install "mcpp@$env:MCPP_PIN" -y -g --verbose

cat "$env:USERPROFILE\.xlings\.xlings.json"
mcpp --version
Expand Down
73 changes: 19 additions & 54 deletions .github/workflows/ci-linux-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: ci-linux-e2e

# The e2e suite (tests/e2e/run_all.sh, ~18 min) split out of ci-linux.yml so it
# runs in PARALLEL with the build/unit/toolchain-matrix job instead of tacked on
# after it. Both workflows share the same cache lineage (mcpp sandbox + xlings +
# target/), so this job restores a warm build and the only added wall-clock vs.
# the inline version is one extra warm `mcpp build`. Net per-PR critical path
# drops from "build + … + e2e" to max(build+matrix, build+e2e).
# The e2e suite (tests/e2e/run_all.sh) split out of ci-linux.yml so it runs in
# PARALLEL with the build/unit/toolchain jobs instead of tacked on after them,
# and SHARDED across two runners on top of that. Both workflows share the same
# cache lineage (mcpp sandbox + xlings + target/), so each shard restores a warm
# build and the only added wall-clock vs. the inline version is one extra warm
# `mcpp build` per runner.
#
# Paired workflows: ci-linux.yml (build + unit + toolchain matrix + integration),
# ci-macos.yml, ci-windows.yml.
# Paired workflows: ci-linux.yml (build + unit + toolchain legs + integration),
# ci-macos.yml / ci-macos-e2e.yml, ci-windows.yml / ci-windows-e2e.yml.

on:
push:
Expand All @@ -23,10 +23,18 @@ concurrency:

jobs:
e2e:
name: e2e suite (linux x86_64, self-host)
name: e2e ${{ matrix.shard }}/2 (linux x86_64, self-host)
runs-on: ubuntu-24.04
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
shard: [1, 2]
env:
# Round-robin slice of tests/e2e (see run_all.sh). Two shards halve the
# suite's wall-clock; the split is computed on the full file list, so a
# test's shard does not move when host capabilities differ.
E2E_SHARD: ${{ matrix.shard }}/2
MCPP_HOME: /home/runner/.mcpp
# NOTE: do NOT force MCPP_VERBOSE here. The e2e suite includes tests that
# assert mcpp's DEFAULT (quiet) output — e.g. 48_build_error_output and
Expand All @@ -37,51 +45,8 @@ jobs:
- uses: actions/checkout@v4

# Same cache lineage as ci-linux.yml so this job lands on a warm
# toolchain/sandbox instead of re-installing it. Both workflows read
# (and may save) the same keys; actions/cache tolerates concurrent
# "already exists" saves.
- name: Cache mcpp sandbox
uses: actions/cache@v4
with:
path: ~/.mcpp
key: mcpp-sandbox-${{ runner.os }}-ci-${{ hashFiles('mcpp.toml', '.xlings.json') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-ci-

- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-v2-

- name: Bootstrap mcpp via xlings
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '0.4.30'
run: |
tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz"
curl -fsSL -o "/tmp/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "/tmp/${tarball}" -C /tmp
"/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
xlings --version
xlings install mcpp -y
MCPP="$HOME/.xlings/subos/default/bin/mcpp"
test -x "$MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"

- name: Cache target/ (build artifacts + BMIs)
uses: actions/cache@v4
with:
path: target
key: mcpp-target-${{ runner.os }}-${{ hashFiles('src/**', 'tests/**', 'mcpp.toml', 'mcpp.lock') }}
restore-keys: |
mcpp-target-${{ runner.os }}-
# toolchain/sandbox instead of re-installing it.
- uses: ./.github/actions/bootstrap-mcpp

- name: Configure mirror + Build mcpp from source (self-host)
run: |
Expand Down
Loading
Loading