# Sandbox CI: the keyless real-kernel confinement proofs. This master-only
# reference stays outside the pull-request verdict; rationale lives in
# .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md.
# A separate workflow from ci.yml because the axis is different — these jobs
# fan out over OS×runner (kernel capabilities), not node versions. The Landlock
# launcher is built from native/landlock-run on each Landlock leg and installed
# from the same tarballs the main-repository release workflow publishes.
name: Sandbox

on:
  push:
    branches: [master]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

env:
  # CI runs must never report to the production telemetry endpoint baked
  # into apps/cli/cordis.yml (AppCLIEntry disables the row when set).
  DSH_TELEMETRY_DISABLED: '1'

jobs:
  # Keyless real-kernel sandbox proofs (sandbox Agent Note § Testing): each ladder
  # rung is only provable on a host where it enforces, so this job fans out
  # an OS×runner matrix — bwrap and Landlock on Linux (separate legs: the
  # Landlock files force the bwrap rung off, so each leg proves exactly one
  # rung; Landlock twice, once per architecture, each confining through the
  # locally built launcher), Seatbelt on macOS (sandbox-exec ships with
  # the OS). One node
  # version only: kernel confinement does not vary by node, and ci.yml's
  # node matrix already covers the node axis.
  #
  # The e2e files self-skip where their runner is absent, so a leg that lost
  # its runner (no bwrap, kernel without Landlock, macOS without
  # sandbox-exec) would otherwise pass as a false green — the same trap
  # e2e.yml's key preflight guards against. Each leg therefore asserts BOTH
  # its platform files actually ran: `Test Files  2 passed (2)`, no skips.
  sandbox-e2e:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            runner: bwrap
          - os: ubuntu-24.04
            runner: landlock
          - os: ubuntu-24.04-arm
            runner: landlock
          - os: macos-latest
            runner: seatbelt
    name: sandbox e2e (${{ matrix.runner }}, ${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v6

      - uses: pnpm/action-setup@v4

      - uses: actions/setup-node@v6
        with:
          node-version: 24

      - name: Install (immutable)
        run: pnpm install --frozen-lockfile

      # The bwrap rung needs bubblewrap on PATH and unprivileged user
      # namespaces. Ubuntu 24.04 gates the latter behind an AppArmor knob;
      # lift it best-effort — on images where the knob is absent the
      # functional probe (and the run-guard below) is the arbiter anyway.
      - name: Install bubblewrap (unrestrict userns)
        if: matrix.runner == 'bwrap'
        run: |
          sudo apt-get update -q
          sudo apt-get install -yq bubblewrap
          sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
            || echo "apparmor userns knob absent — the functional probe decides"

      - name: Build Landlock launcher for this architecture
        if: matrix.runner == 'landlock'
        run: |
          sudo apt-get update -q
          sudo apt-get install -yq musl-tools
          pnpm --dir native/landlock-run run build:ts
          pnpm --dir native/landlock-run run build:native

      # The unit suite runs on ubuntu in `checks`; this is the one darwin leg
      # in the workflow, so run it here too — the platform-dependent unit
      # expectations (Seatbelt path canonicalization: /tmp IS /private/tmp)
      # take their darwin branch only on this runner.
      - name: Unit tests (darwin parity)
        if: matrix.runner == 'seatbelt'
        run: pnpm run test

      - name: Sandbox e2e (real kernel confinement, world-verified)
        # NO_COLOR: vitest force-enables ANSI color under GITHUB_ACTIONS even
        # without a TTY, which would thread escape codes through the summary
        # line the run-guard greps.
        env:
          NO_COLOR: 1
        run: |
          set -u +e -o pipefail
          out=$(pnpm exec vitest run --config vitest.e2e.config.ts \
            packages/sandbox/sandbox-local/tests/${{ matrix.runner }}.e2e.ts \
            packages/shell/bash-sandbox/tests/${{ matrix.runner }}.e2e.ts 2>&1); status=$?
          echo "$out"
          [ "$status" -eq 0 ]
          # Both platform files must have RUN — a self-skip (runner missing on
          # the very platform that exists to prove it) is a failure, not a pass.
          echo "$out" | grep -qE 'Test Files[[:space:]]+2 passed \(2\)'

      # Publish-path rehearsal, Landlock legs only. Build the launcher on its
      # native architecture, then install the local native and harness tarballs
      # together so registry state cannot mask source/package drift.
      - name: Build packages for the pack rehearsal
        if: matrix.runner == 'landlock'
        run: pnpm run build:official

      - name: Packed-distribution e2e (pack → install → confine)
        if: matrix.runner == 'landlock'
        env:
          NO_COLOR: 1
        run: |
          set -u +e -o pipefail
          out=$(pnpm exec vitest run --config vitest.e2e.config.ts \
            packages/sandbox/sandbox-local/tests/packed-install.e2e.ts 2>&1); status=$?
          echo "$out"
          [ "$status" -eq 0 ]
          echo "$out" | grep -qE 'Test Files[[:space:]]+1 passed \(1\)'
