Skip to content

test(e2e): classify hermetic test worlds as unclassified context scope #159

test(e2e): classify hermetic test worlds as unclassified context scope

test(e2e): classify hermetic test worlds as unclassified context scope #159

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
env:
# PyMuPDF 1.27.2 / SWIG #2881: exact Python 3.13 shutdown warning only.
# Import-time warnings are repaired in pymupdf_compat; remove this when
# upstream generated types carry __module__ metadata.
PYTHONWARNINGS: "ignore:builtin type swigvarlink has no __module__ attribute:DeprecationWarning"
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- run: uv sync --locked --all-packages --group dev
- run: uv run --group dev ruff check .
- run: uv run --group dev ruff format --check .
typecheck:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- run: uv sync --locked --all-packages --group dev
- run: just typecheck
sast:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- run: uv sync --locked --all-packages --group dev
- name: Run bandit (SAST)
# Skip list mirrors .pre-commit-config.yaml's bandit hook, which
# documents each rule. B608 (dynamic SQL) in particular is skipped
# project-wide because every hit was verified individually in the
# 2026-09 security review: each interpolates only a server-side
# allowlisted column name or a `?`-placeholder count, never raw
# user data; actual values always go through bound parameters.
run: uv run --group dev bandit --recursive --skip B101,B104,B105,B106,B107,B108,B110,B310,B404,B603,B606,B607,B608 packages/
audit:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- run: uv sync --locked --all-packages --group dev
- name: Audit dependencies for known vulnerabilities
run: |
uv --quiet export --all-packages --group dev --no-emit-workspace --format requirements-txt -o /tmp/requirements.txt
uv tool run pip-audit -r /tmp/requirements.txt --strict --no-deps --disable-pip
audit-full:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- run: uv sync --locked --all-packages --group dev --all-extras
- name: Audit full optional dependency surface
run: |
uv --quiet export --all-packages --group dev --all-extras --no-emit-workspace --format requirements-txt -o /tmp/requirements-full.txt
uv tool run pip-audit -r /tmp/requirements-full.txt --strict --no-deps --disable-pip
test:
runs-on: ubuntu-latest
timeout-minutes: 25
strategy:
fail-fast: false # report every Python version independently; don't cancel siblings
matrix:
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: ${{ matrix.python-version }}
- run: uv sync --locked --all-packages --group dev
- run: uv run --group dev pytest --tb=short --cov=packages --cov-report=term-missing
js:
# The browser-side unit tests (87 at time of writing) previously ran in NO
# gate: no workflow and no just recipe invoked them, so they only executed if
# someone typed `node --test` by hand. A suite nothing runs cannot be evidence
# of anything, which is the same blind spot that hid three regressions in the
# unrun e2e suites.
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
- run: node --test packages/studyloop/tests/js/*.test.js
web-profile:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- run: uv sync --locked --all-packages --group dev --extra web
- run: just test-web
browser-smoke:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- run: uv sync --locked --all-packages --group dev --extra web
- run: uv run playwright install --with-deps chromium
env:
# Playwright 1.58 still calls Node's deprecated url.parse during its
# installer only. Suppress that exact upstream warning code here;
# application/test Node warnings remain visible.
NODE_OPTIONS: "--disable-warning=DEP0169"
- run: just test-browser-smoke
e2e:
# The other 39 e2e files. Until this job existed only the single smoke file
# above ran in any gate, so ~500 browser tests were evidence of nothing --
# the same blind spot called out for the JS tests in the `js` job. Two real
# defects were hiding in there: a 500 from a file-deletion race in session
# teardown, and a 200ms sleep racing a 187ms CSS fade.
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
# tmux, but deliberately NOT ttyd. This mirrors the environment where the
# 500-pass unscaled result was actually verified, which had tmux and no
# ttyd. ttyd's absence is itself under test --
# test_terminal_proxy_degrades_when_ttyd_is_absent asserts the proxy
# answers 502 rather than hanging -- so installing it would diverge CI
# from the proven configuration and weaken that contract. Do not "fix"
# this by adding ttyd.
- run: sudo apt-get update && sudo apt-get install -y tmux
- run: uv sync --locked --all-packages --group dev --extra web
- run: uv run playwright install --with-deps chromium
env:
# Playwright 1.58 still calls Node's deprecated url.parse during its
# installer only. Suppress that exact upstream warning code here;
# application/test Node warnings remain visible.
NODE_OPTIONS: "--disable-warning=DEP0169"
# A missing dependency makes this suite SKIP rather than fail, so "0
# failed" would survive a run that executed almost nothing. The floor is
# set below the 500 currently passing so ordinary growth or a legitimate
# new skip does not trip it, while a collapse to a hollow green does.
- run: just e2e
env:
STUDYLOOP_MIN_PASSED: "450"
# The e2e tests already screenshot the page and dump its HTML when they
# fail (_diag in the journey files), but nothing collected those, so every
# CI failure had to be diagnosed from a stack trace alone -- a
# wait_for_function timeout says only that a condition never became true,
# never what the page actually showed instead. Uploading them turns the
# next failure into evidence rather than a hypothesis.
#
# if: failure() rather than always(): on a green run there is nothing to
# collect, and an empty artifact per run is noise.
- if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-failure-diagnostics
path: |
test-results/
packages/studyloop/test-results/
retention-days: 7
if-no-files-found: warn
content-profile:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- run: uv sync --locked --all-packages --group dev --extra content
- run: just test-content
semantic-profile:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- run: uv sync --locked --all-packages --group dev --extra semantic
- run: just test-semantic
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- run: ./scripts/build-release.sh
- run: uv run python scripts/check-release-consistency.py
install-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: build
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
- name: Build and smoke-test installed wheel
run: |
./scripts/build-release.sh
tmp="$(mktemp -d)"
uv venv "$tmp/venv"
uv pip install --python "$tmp/venv/bin/python" dist/studyloop-*.whl packages/agent-session-tools
STUDYLOOP_EXPECT_BIN_DIR="$tmp/venv/bin" PATH="$tmp/venv/bin:$PATH" ./scripts/smoke-installed-cli.sh