Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
24035aa
feat(mps): add Apple Silicon MPS backend with deadlock-safe benchmarking
Akasxh May 1, 2026
4ede763
feat(fuzzing): stride and contiguity fuzzing for GPU kernels
Akasxh May 1, 2026
5ddd26e
fix(tolerances): thread-safe override stack via contextvars; mitigate…
Akasxh May 1, 2026
02507da
feat(reporting+sanitizers): HTML dashboard, determinism, lockfile, CI…
Akasxh May 1, 2026
cc81650
Merge branch 'feat/track-a-mps' into release/v1.0
Akasxh May 1, 2026
472fd91
Merge branch 'feat/track-b-strides' into release/v1.0
Akasxh May 1, 2026
0c44e74
Merge branch 'feat/track-d-bundle' into release/v1.0
Akasxh May 1, 2026
a60e3a5
chore(lock): regenerate uv.lock post-merge to include Track-A [mps] e…
Akasxh May 1, 2026
85de0f9
docs(changelog): add Keep-a-Changelog 1.1 with v1.0.0rc1 release notes
Akasxh May 1, 2026
195779b
docs(contributing): add development guide with uv, MPS, conventional …
Akasxh May 1, 2026
2673211
docs(migration): add v0.1.0 -> v1.0 migration guide
Akasxh May 1, 2026
40ba1de
docs(readme): replace CUDA-only language with CUDA + MPS, add v1.0 po…
Akasxh May 1, 2026
6a07ca6
docs(claude.md): reflect v1.0 delivery — MPS, strides, thread-safety,…
Akasxh May 1, 2026
82b853e
fix(fuzzing): drop unused # type: ignore on @st.composite decorator
Akasxh May 1, 2026
6bdccb8
fix(sanitizers): rename _MutableReport to public MemoryGuardReport (T…
Akasxh May 7, 2026
9f430f7
refactor(assertions): make torch lazy-imported per CLAUDE.md contract…
Akasxh May 7, 2026
174cb6a
fix(backends,arch): narrow bare-except per CLAUDE.md no-bare-except c…
Akasxh May 7, 2026
25fd515
fix(tests): auto-skip gpu_integration suite when no usable GPU detect…
Akasxh May 7, 2026
aeedbdc
fix(plugin): narrow pyproject.toml load exceptions and warn on parse …
Akasxh May 7, 2026
1dd7ba9
fix(assertions): contiguous() on slow path for stride-fuzzed tensors …
Akasxh May 7, 2026
da4280d
refactor(arch,fixtures): consolidate gpu detection helper (T-20 — det…
Akasxh May 7, 2026
ff79f49
refactor(arch): introduce requires_arch alias for naming consistency …
Akasxh May 7, 2026
b500339
test(assertions): pin numeric fields in mismatch report (T-10 — kills…
Akasxh May 7, 2026
47ee38f
chore(executor-log): record commit SHAs for T-01/T-02/T-10 close.py g…
Akasxh May 7, 2026
37e1b5d
fix(assertions): canonical sqrt(K/128) scaling in baseline_2x path (B…
Akasxh May 7, 2026
fdeaea1
fix(fuzzing): accept kebab and snake stride category names (B-A1 — re…
Akasxh May 7, 2026
0da3fd4
fix(deps): declare tomli as conditional dep for Python 3.10 fallback …
Akasxh May 7, 2026
ecd9961
fix(sanitizers,docs): add atol kwarg to assert_deterministic + correc…
Akasxh May 7, 2026
93895ef
chore(deps): regenerate uv.lock for tomli conditional dep (B-S1)
Akasxh May 7, 2026
f39a0c0
docs(tolerances): point to v1.1 calibration data + T-24 deferred refa…
Akasxh May 7, 2026
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
111 changes: 111 additions & 0 deletions .claude/teams/audit/v1.1/EVIDENCE/executor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Executor — close.py collision group (T-01, T-02, T-10)

Branch: release/v1.0
Owner: engineering-executor
Mutmut baseline at start: 169 killed mutants
Pre-existing test count at start: 225 collected (224 passed + 1 skipped)

## Task T-01 (Lazy torch import)

### What I did
Replaced the module-level `try: import torch as _torch / _has_torch` block in
`src/gpucheck/assertions/close.py` with a lazy `_torch_mod()` helper that
caches the imported module (or `None`) using a sentinel. Both call sites
inside `assert_close` (device-type detection and the GPU fast-path) now bind
`_torch = _torch_mod()` once at the top of the function and guard with
`_torch is not None`.

### Files modified
- `src/gpucheck/assertions/close.py`: lazy-import refactor; `_has_torch` removed; `_torch_mod()` is the single point of access.

### Files created
- `DIFF_LOG.md` (this iteration logs the T-01 change).
- `.claude/teams/audit/v1.1/EVIDENCE/executor.md` (this file).

### Design decisions made during implementation
- Used a sentinel (`_TORCH_UNRESOLVED = object()`) instead of `None` for the
cache initial state, so a process where torch is genuinely absent still
short-circuits after the first probe (caches `None` permanently).
- Kept the inner `import torch` inside the `__cuda_array_interface__`
fallback as-is rather than routing it through `_torch_mod()`. That branch
needed torch *and* dlpack to be present and was already lazy by virtue of
living inside a function body — re-routing through the helper would have
changed behaviour (it would no longer except `ImportError` locally).

### Potential blast radius
- If `gpucheck.assertions.close` is imported on a torch-less host, the
module load path no longer raises or warns. Behaviour matches the
previous code (which set `_has_torch = False` silently).
- The `_torch_cached` global is process-wide. If a test fixture
monkey-patches `sys.modules['torch']` after `_torch_mod()` has run once,
the cached value will be stale. Existing tests do not do this, but the
verifier should confirm.

## Task T-02 (`.contiguous()` on slow path)

### What I did
Added `.contiguous()` to both torch.Tensor branches of `_to_numpy` (the
primary `hasattr(tensor, "detach")` branch and the dlpack fallback in the
`__cuda_array_interface__` block). Wrote a new parametrized test module
`tests/test_assert_close_contiguous.py` covering three stride patterns:
slice (`[:, ::2]`), transpose (`.t()`), and broadcast (`.expand`).

### Files modified
- `src/gpucheck/assertions/close.py`: two `.cpu().contiguous()` insertions; PM-4 citation comment.

### Files created
- `tests/test_assert_close_contiguous.py`: 6 parametrized cases (3 stride patterns × 2 entry points).

### Design decisions made during implementation
- Applied `.contiguous()` to the dlpack fallback as well even though only
the primary branch is in the strict T-02 scope. The same RuntimeError
surface exists in both code paths and the cost is negligible. Noted
here as an opportunistic widen so the reviewer can flag if undesired.
- Used `pytest.importorskip("torch")` rather than the existing
`_has_torch`-style guard so the module skips cleanly on torch-less
hosts (matches the lazy-import discipline from T-01).

### Potential blast radius
- `.contiguous()` allocates a new tensor when the input is non-contiguous.
For very large stride-fuzzed tensors this could double peak memory in
the slow path. Existing CUDA fast-path (which bypasses `_to_numpy`)
already handles same-shape tensors without copying, so the regression is
bounded to mismatched / failing comparisons.

## Task T-10 (Pin numeric fields in mismatch report)

### What I did
Added `TestMismatchReportPinnedNumerics` to `tests/test_assertions.py` with
three new tests. Each test constructs simple integer-valued numpy inputs so
expected values can be hand-computed exactly, then asserts those values
appear verbatim (with `:.6e` formatter) in the rendered Rich report. Did
NOT modify `src/gpucheck/assertions/reporting.py` per task instructions.

### Files modified
- `tests/test_assertions.py`: appended 3 tests targeting the ~30 surviving
reporting.py mutants from `EVIDENCE/mutator-survivors.md`.

### Files created
- (none)

### Design decisions made during implementation
- Used 2-D input in test 2 specifically so `np.unravel_index` is exercised
meaningfully (with a 1-D input, any axis-mutation would be a no-op).
- For the histogram count assertion, stripped ANSI escape codes via
`re.sub(r"\x1b\[[0-9;]*m", "", report)` before scanning digits because
Rich's coloured output otherwise leaks digits like `33`/`31` from
``\x1b[33m`` and pollutes the digit-only filter.
- Selected unique-maximum diff values (4.5, 5.0) so the location index
is unambiguous — eliminating spurious passes if `nanargmax` is mutated
to e.g. `nanargmin` and the answer happens to coincide.

### Potential blast radius
- The histogram-count test depends on the bar-rendering loop emitting
the count after the bar (`f" {bucket:>22s} | {bar} {count}"`). If the
format string is reordered (count before bar), the test would still
isolate the digits via the line-tail extraction, but the failure
message would be misleading. Acceptable trade-off for now.
- `5 / 6 (83.33%)` substring is whitespace-sensitive: if the table
formatter switches columns or pads differently, the test could
false-fail. Task scope is to test current behaviour; if reporting is
refactored, these tests must be updated.
39 changes: 32 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ on:
pull_request:
branches: [main]

# Security finding CFG-2 (FINDINGS.md): default GITHUB_TOKEN scope is
# write-all. Explicitly restrict to read-only at the workflow level so
# nothing in this CI job can accidentally publish artifacts or comments
# unless a step elevates locally.
permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
Expand All @@ -14,12 +21,22 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install dependencies (locked)
# Use the committed uv.lock for reproducibility and supply-chain
# protection (security finding DEP-1).
run: |
uv venv
if [ -f uv.lock ]; then
uv sync --frozen --extra dev
else
uv pip install -e ".[dev]"
fi
- name: Ruff check
run: ruff check src/ tests/
run: uv run ruff check src/ tests/
- name: Mypy
run: mypy src/
run: uv run mypy src/

test:
runs-on: ubuntu-latest
Expand All @@ -32,7 +49,15 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install dependencies (locked)
run: |
uv venv
if [ -f uv.lock ]; then
uv sync --frozen --extra dev
else
uv pip install -e ".[dev]"
fi
- name: Run tests
run: pytest --tb=short -q
run: uv run pytest --tb=short -q
Loading
Loading