Red CI fixed, wind-down decision layer, and the ruled 0.2.1 work #142
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| - run: uv sync --locked --all-packages --group dev | |
| - run: just typecheck | |
| sast: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 | |
| with: | |
| node-version: "22" | |
| - run: node --test packages/studyloop/tests/js/*.test.js | |
| web-profile: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| - run: uv sync --locked --all-packages --group dev --extra web | |
| - run: uv run playwright install --with-deps chromium | |
| - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| # 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 | |
| # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b | |
| - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@e4db8464a088ece1b920f60402e813ea4de65b8f # v4 | |
| 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 |