Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ _*/
**/AGENTS.local.md
**/CLAUDE.local.md

.cache

# Generated model-specific vectors; retain the source asset banks.
src/usersim/engine/assets/financial_services/**/embeddings.parquet
**/junit.xml
7 changes: 6 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@
/tests/test_ignore_policy.py @NVIDIA-NeMo/user_sim_maintainers
/tests/test_update_license_headers.py @NVIDIA-NeMo/user_sim_maintainers
/docs/engineering/ @NVIDIA-NeMo/user_sim_maintainers
/tests/fixtures/exclusion-policy.json @NVIDIA-NeMo/user_sim_maintainers
/containers/ @NVIDIA-NeMo/user_sim_maintainers
/.dockerignore @NVIDIA-NeMo/user_sim_maintainers
/tests/test_container_runner.py @NVIDIA-NeMo/user_sim_maintainers
/tests/test_coverage_gate.py @NVIDIA-NeMo/user_sim_maintainers
/tests/test_check_ci_results.py @NVIDIA-NeMo/user_sim_maintainers
/tests/test_release_validation_workflow.py @NVIDIA-NeMo/user_sim_maintainers

/.mise.toml @NVIDIA-NeMo/user_sim_maintainers
/mise.lock @NVIDIA-NeMo/user_sim_maintainers
/.mise/ @NVIDIA-NeMo/user_sim_maintainers

/tests/fixtures/exclusion-policy.json @NVIDIA-NeMo/user_sim_maintainers
107 changes: 69 additions & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Every job here wraps a `make` target rather than restating the command, so
# Every job here calls a shared Mise task rather than restating the command, so
# what CI enforces and what you can run locally cannot drift apart. If a job
# fails, the same target reproduces it on your machine.
#
Expand All @@ -25,6 +25,7 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
UV_PYTHON: "3.12"
PYTEST_WORKERS: "2"
NEMO_TELEMETRY_ENABLED: "false"

Expand All @@ -36,19 +37,23 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
enable-cache: true
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools

- run: make install-ci
- run: scripts/mise run setup:ci

- name: Static checks and all tracked-file hooks
run: make check-all
run: scripts/mise run check

- name: Dependency licences
run: make check-dependency-licenses
run: scripts/mise run check:license:dependencies


typecheck:
Expand All @@ -57,15 +62,21 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
enable-cache: true
- run: make install-ci
- run: make typecheck
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools
- run: scripts/mise run setup:ci
- run: scripts/mise run check:type

test:
env:
UV_PYTHON: ${{ matrix.python-version }}
name: Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 30
Expand All @@ -76,24 +87,32 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: ${{ matrix.python-version }}
enable-cache: true
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools

- run: make install-ci
- run: scripts/mise run setup:ci

- name: Test suite
if: matrix.python-version != '3.12'
# Durations are reported because a slow test is invisible until
# someone profiles the suite, and the runner is the only place that
# measures the hardware CI actually runs on.
run: make test PYTEST_ARGS="--durations=25 --junitxml=junit.xml"
env:
PYTEST_ARGS: "--durations=25 --junitxml=junit.xml"
run: scripts/mise run test

- name: Test suite with coverage
if: matrix.python-version == '3.12'
run: make coverage PYTEST_ARGS="--durations=25 --junitxml=junit.xml"
env:
PYTEST_ARGS: "--durations=25 --junitxml=junit.xml"
run: scripts/mise run test:ci

- name: Coverage report
if: always() && matrix.python-version == '3.12'
Expand All @@ -114,12 +133,12 @@ jobs:
retention-days: 14

- name: Offline self-check
run: make smoke
run: scripts/mise run test:smoke

- name: Dependency licences
# Each Python resolves its own releases of some dependencies, and the
# policy approves exceptions per release and reported licence.
run: make check-dependency-licenses
run: scripts/mise run check:license:dependencies

packaging:
name: Wheel and extension seams
Expand All @@ -128,33 +147,43 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
enable-cache: true
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools

- run: make install-ci
- run: scripts/mise run setup:ci

- name: Build and check the sdist and wheel, install the wheel clean, run it from an empty directory
run: make check-dist
run: scripts/mise run check:dist

- name: An out-of-tree package reaches every extension seam
run: make check-extensions
run: scripts/mise run check:extensions

e2e:
name: Installed CLI e2e
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
enable-cache: true
- run: make install-ci
- run: make test-e2e E2E_ARGS="--artifacts test-results/e2e"
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools
- run: scripts/mise run setup:ci
- env:
E2E_ARGS: "--artifacts test-results/e2e"
run: scripts/mise run test:e2e
- name: E2e diagnostics on failure
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
Expand All @@ -170,9 +199,11 @@ jobs:
timeout-minutes: 65
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh

- name: Build and run isolated CPU validation
run: make test-container
run: scripts/mise run test:ci-container

- name: Container test diagnostics
if: always()
Expand All @@ -194,4 +225,4 @@ jobs:
CI_JOB_RESULTS: ${{ toJSON(needs) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: make check-ci-status
- run: python3 scripts/check_ci_results.py
24 changes: 14 additions & 10 deletions .github/workflows/health-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ concurrency:
group: health-checks
cancel-in-progress: false

env:
UV_PYTHON: "3.12"

jobs:
live:
name: ${{ matrix.provider }} end to end
Expand Down Expand Up @@ -66,13 +69,17 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Cache locked Python dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
enable-cache: true
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ runner.arch }}-${{ env.UV_PYTHON }}-${{ hashFiles('uv.lock', 'mise.lock') }}
- name: Locked registry tools
run: scripts/mise run setup:tools

- run: make install-ci
- run: scripts/mise run setup:ci

- name: Simulate, evaluate and report
env:
Expand All @@ -90,11 +97,8 @@ jobs:
HEALTH_LOCALE: ${{ matrix.locale }}
run: |
export "$KEY_ENV=$PROVIDER_API_KEY"
make health-check \
PROVIDER_CONFIG="$PROVIDER_CONFIG" \
HEALTH_LOCALE="$HEALTH_LOCALE" \
HEALTH_SUMMARY_FILE=/tmp/usersim-health-summary.md \
| tee /tmp/health.log
export HEALTH_SUMMARY_FILE=/tmp/usersim-health-summary.md
scripts/mise run test:health | tee /tmp/health.log

- name: Summary
if: always()
Expand Down
28 changes: 15 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

env:
UV_PYTHON: "3.12"

jobs:
build:
name: Build and check
Expand All @@ -39,38 +42,37 @@ jobs:
fetch-depth: 0
persist-credentials: false

- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version-file: "pyproject.toml"
python-version: "3.12"
# A cache written by another run could otherwise reach the
# published files.
enable-cache: false
- name: Verified Mise bootstrap
run: bash scripts/install_mise.sh
- name: Locked registry tools
run: scripts/mise run setup:tools

- run: make install-ci
- run: scripts/mise run setup:ci

- name: Native checks, coverage, smoke, and extension contracts
# Re-run the shared gates on the tagged commit, even if PR CI passed.
run: make validate-native
run: scripts/mise run validate:native

- name: Build and check the sdist and wheel, install the wheel clean, run it from an empty directory
run: make check-dist
run: scripts/mise run check:dist

- name: The tag names the built release version and a commit on main
id: tag
env:
# In the environment, not on make's command line: make would expand
# a `$(shell ...)` in the tag name there.
TAG: ${{ github.ref_name }}
run: make check-release-tag
run: scripts/mise run check:release-tag

- name: Installed CLI e2e against the original release wheel
# check-dist produced and validated this single wheel. Do not rebuild.
run: make test-e2e WHEEL_DIR=dist
env:
WHEEL_DIR: dist
run: scripts/mise run test:e2e --wheel-dir dist

- name: SHA-256 digests for publication approval
# Available from the read-only build before the release environment gate.
run: make release-digests
run: scripts/mise run release:digests

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,8 @@ CLAUDE.local.md
.codex/config.local.toml
.worktrees/

# Repository-scoped Mise installations and runtime state
.cache/

# Local agent working notes
/.agent-work/
17 changes: 17 additions & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Mise owns the uv release; Python quality tools remain owned by uv.lock.
min_version = "2026.8.5"

[tools]
uv = "0.13.1"

[task_config]
includes = [".mise/tasks", ".mise/tasks/*.toml"]

[settings]
lockfile = true
lockfile_platforms = ["linux-x64", "linux-arm64", "macos-arm64", "macos-x64"]
task.run_auto_install = false
idiomatic_version_file_enable_tools = []
24 changes: 24 additions & 0 deletions .mise/tasks/check/dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

#MISE description="Validate archives and a clean wheel installation"
#MISE depends=["build:dist"]

set -euo pipefail
cd "${MISE_CONFIG_ROOT:?}"
"${UV:-uv}" run python scripts/check_dist.py dist; \
WORK=$(mktemp -d); \
EMPTY=$(mktemp -d); \
trap 'rm -rf "$WORK" "$EMPTY"' EXIT; \
echo "→ installing the wheel into a throwaway venv"; \
"${UV:-uv}" venv "$WORK/venv" >/dev/null; \
test -x "$WORK/venv/bin/python" || { echo "venv was not created"; exit 1; }; \
"${UV:-uv}" pip install --quiet --python "$WORK/venv/bin/python" dist/*.whl; \
test -x "$WORK/venv/bin/usersim" \
|| { echo " ✗ the wheel installed but shipped no usersim entry point"; exit 1; }; \
echo "→ running from $EMPTY (no repo in sight)"; \
cd "$EMPTY" && "$WORK/venv/bin/usersim" smoke; \
cd "$EMPTY" && "$WORK/venv/bin/usersim" simulate --dry-run \
--locale en_US --num-rows 1 --out ./out >/dev/null; \
echo "✓ installed package runs from a clean environment"
Loading
Loading