Skip to content

fix(development-system): preserve checkpoint review invariants #621

fix(development-system): preserve checkpoint review invariants

fix(development-system): preserve checkpoint review invariants #621

Workflow file for this run

name: CI
# Public repo: pull_request is reachable from forks, so every job below stays
# on GitHub-hosted Ubuntu runners. Direct pushes to trunk run the same gate,
# and merge_group preserves coverage for repositories that opt into PR mode.
on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:
types: [checks_requested]
permissions:
contents: read
# Cancel only an older revision of the same delivery stream. Event type keeps
# main pushes, pull requests, and merge-queue validation isolated; ref keeps
# separate pull requests and merge groups from canceling one another.
concurrency:
group: ci-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Quality gate
# Ubuntu 24.04's AppArmor policy strips capabilities from unprivileged user
# namespaces, so the Nix Bubblewrap binary cannot construct the verifier's
# mount namespace. Keep the sandbox tests on the supported 22.04 image.
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Cache Nix store
uses: DeterminateSystems/magic-nix-cache-action@main
- name: Provision delegated user systemd
run: |
uid="$(id -u)"
user="$(id -un)"
runtime_dir="/run/user/$uid"
delegate_source="$RUNNER_TEMP/ai-plugins-user-delegate.conf"
delegate_drop_in="/run/systemd/system/user@${uid}.service.d/ai-plugins-ci.conf"
user_manager_unit="user@$uid.service"
runtime_unit="user-runtime-dir@$uid.service"
sudo systemctl is-active --quiet systemd-logind.service
# Delegation is allocation-time state, so Jammy cannot apply it with
# `systemctl set-property` after the user manager already exists.
printf '%s\n' \
'[Service]' \
'Delegate=cpu memory pids' \
'LimitSTACK=67108864' > "$delegate_source"
sudo install -D -m 0644 "$delegate_source" "$delegate_drop_in"
sudo systemctl daemon-reload
# Load the override before enabling linger so an activation raced by
# logind cannot start the manager with the old delegation contract.
sudo loginctl enable-linger "$user"
activate_user_manager() {
if sudo systemctl is-active --quiet "$user_manager_unit"; then
sudo systemctl restart "$user_manager_unit" || return
else
sudo systemctl reset-failed "$user_manager_unit" "$runtime_unit" || true
sudo systemctl start "$user_manager_unit" || return
fi
sudo systemctl is-active --quiet "$user_manager_unit"
}
diagnose_user_manager() {
sudo systemctl status --no-pager --full \
"$user_manager_unit" "$runtime_unit" || true
sudo systemctl show "$user_manager_unit" "$runtime_unit" \
--property=ActiveState,SubState,Result,ExecMainStatus,ControlGroup,DelegateControllers || true
sudo journalctl --boot --no-pager --lines=100 \
--unit="$user_manager_unit" --unit="$runtime_unit" || true
}
activation_succeeded=false
for attempt in 1 2 3; do
if activate_user_manager; then
activation_succeeded=true
break
fi
echo "user systemd manager activation attempt $attempt failed" >&2
diagnose_user_manager
sudo systemctl reset-failed "$user_manager_unit" "$runtime_unit" || true
if [ "$attempt" -lt 3 ]; then
sleep "$attempt"
fi
done
if [ "$activation_succeeded" != true ]; then
exit 1
fi
delegated_controllers="$(
sudo systemctl show "$user_manager_unit" \
--property=DelegateControllers --value
)"
for controller in cpu memory pids; do
case " $delegated_controllers " in
*" $controller "*) ;;
*)
echo "user systemd manager is missing delegated $controller control" >&2
exit 1
;;
esac
done
# The trusted Rust scorer caps compiler subprocesses at a 64 MiB
# stack. GitHub's runner session supplies a lower hard limit, which
# an unprivileged user manager cannot raise for its own units.
manager_stack_limit="$(
sudo systemctl show "$user_manager_unit" \
--property=LimitSTACK --value
)"
[ "$manager_stack_limit" = 67108864 ] || {
echo "user systemd manager has stack limit $manager_stack_limit; expected 67108864" >&2
exit 1
}
# Ubuntu's PAM stack can reapply the runner session's lower hard
# limit after PID 1 applies the unit property. Correct and verify
# the live manager so it can honor the transient gate service's
# explicit 64 MiB limit.
manager_pid="$(
sudo systemctl show "$user_manager_unit" \
--property=MainPID --value
)"
case "$manager_pid" in
''|*[!0-9]*|0)
echo "user systemd manager has invalid MainPID: $manager_pid" >&2
exit 1
;;
esac
sudo prlimit --pid "$manager_pid" --stack=67108864:67108864
read -r manager_stack_soft manager_stack_hard manager_stack_units < <(
sudo prlimit --pid "$manager_pid" --stack --raw --noheadings \
--output=SOFT,HARD,UNITS
)
[ "$manager_stack_soft" = 67108864 ] || {
echo "user systemd manager has soft stack limit $manager_stack_soft; expected 67108864" >&2
exit 1
}
[ "$manager_stack_hard" = 67108864 ] || {
echo "user systemd manager has hard stack limit $manager_stack_hard; expected 67108864" >&2
exit 1
}
[ "$manager_stack_units" = bytes ]
test ! -L "$runtime_dir"
test "$(stat -c '%u:%a' "$runtime_dir")" = "$uid:700"
test -S "$runtime_dir/systemd/private"
XDG_RUNTIME_DIR="$runtime_dir" systemctl --user show-environment >/dev/null
echo "XDG_RUNTIME_DIR=$runtime_dir" >> "$GITHUB_ENV"
- name: Full gate
run: |
runtime_dir="$XDG_RUNTIME_DIR"
nix_bin="$(command -v nix)"
# Command substitutions in the single-quoted payload intentionally
# expand in the inner Bash process, not in this workflow shell.
# shellcheck disable=SC2016
XDG_RUNTIME_DIR="$runtime_dir" \
systemd-run \
--user \
--wait \
--pipe \
--quiet \
--collect \
--unit="ai-plugins-ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
--property=Type=exec \
--property=LimitSTACK=67108864 \
--working-directory="$GITHUB_WORKSPACE" \
--setenv=PATH="$PATH" \
--setenv=HOME="$HOME" \
--setenv=CI=true \
--setenv=GITHUB_ACTIONS=true \
--setenv=XDG_RUNTIME_DIR="$runtime_dir" \
"$nix_bin" develop -c bash -eu -o pipefail -c '
[ "$(ulimit -Hs)" = 65536 ] || {
echo "full gate hard stack limit is $(ulimit -Hs) KiB; expected 65536" >&2
exit 1
}
bats --print-output-on-failure \
--filter "scorer contains its full process tree in the fixed aggregate systemd scope|direct public verifier uses the fixed aggregate scope without ambient secrets" \
scripts/tests/evals-code-quality-scorer-security.bats
just ci
'
codex-manifests:
name: Codex manifests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Cache Nix store
uses: DeterminateSystems/magic-nix-cache-action@main
# Structural check: every Codex manifest is valid. Full `codex`-CLI
# runtime verification runs where a Codex login is available.
- name: Codex manifests are valid JSON
run: |
nix develop -c bash -c '
jq empty .agents/plugins/marketplace.json
find plugins -path "*/.codex-plugin/plugin.json" -exec jq empty {} \;
'
eval-config:
name: Eval config dry-run
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Cache Nix store
uses: DeterminateSystems/magic-nix-cache-action@main
- name: Validate provider-backed eval configuration
run: |
nix develop -c scripts/evals/run.sh --dry-run
nix develop -c scripts/evals/run.sh --suite canary --dry-run
# Single aggregate status: succeeds only when every required job did.
gate:
name: CI gate
runs-on: ubuntu-latest
needs: [quality, codex-manifests, eval-config]
if: always()
steps:
- name: Require all required jobs to have succeeded
run: |
required="${{ needs.quality.result }} ${{ needs.codex-manifests.result }} ${{ needs.eval-config.result }}"
echo "required job results: $required"
for result in $required; do
if [ "$result" != "success" ]; then
echo "a required job did not succeed: $result"
exit 1
fi
done