gpucheck v1.0 — Apple MPS backend, stride fuzzing, dashboard - #2
Merged
Conversation
Track-A of the gpucheck v1.0 release introduces an MPS backend wired through a structural Backend Protocol. The benchmark fixture uses device-level torch.mps.synchronize() instead of per-event Event sync to avoid the deadlock documented in pytorch#162872 (research SYNTHESIS §3). Major additions: - New gpucheck.backends package with Backend / EventTimer Protocols and CUDABackend / MPSBackend implementations. - @Devices("mps") and @Devices("all") parametrize across CUDA + MPS. - assert_close GPU fast-path widened to MPS tensors (no CPU transfer). - compute_tolerance accepts device_type="mps" and applies a PROVISIONAL 2x dtype-aware multiplier (calibration plan in SYNTHESIS §7). - [tool.gpucheck.mps.xfail] config block in pyproject.toml ships the 12 known-broken kernels from SYNTHESIS §2 as a living document. - gpucheck.is_mps_xfailed("op.subcategory") queries the xfail registry. - pyproject.toml extras: [mps] and [apple] (torch>=2.6 floor). - GPUInfo gains a backend: str field; MPS-derived instances populate architecture="Apple-Silicon", compute_capability=(0, 0), tensor_core_generation=None. - gpu_benchmark fixture branches on cuda_avail vs mps_avail; MPS path uses time.perf_counter() between torch.mps.synchronize() calls. Security findings touched: - N1 / N2 (xcrun metal subprocess hardening): out-of-scope, gpucheck does not shell out to xcrun (PyTorch handles that internally). - N3 (mach task_info): out-of-scope, MPS memory comes from torch.mps.current_allocated_memory and psutil RSS. - N4 (supply chain): partially mitigated via torch>=2.6 floor on [mps]. - N5 (MPS dispatch sanitizer): out-of-scope until Apple ships one. Test count: 117 baseline -> 147 passing (+30 net new MPS-aware tests). ruff and mypy strict pass clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Track-B of the gpucheck v1.0 release fills the documented "no stride /
contiguity fuzzing" gap (CLAUDE.md weaknesses). Adds a deterministic
seven-category corpus and a Hypothesis StrideStrategy, wired into
parametrize_gpu via a new stride_categories= keyword.
Categories (priority order):
row_major, column_major, broadcast, transpose, slice, non_contig, gather
Each category exercises a different code path inside PyTorch's kernel
dispatcher. Test authors can write:
@parametrize_gpu(
dtypes=("float32",), shapes=((64, 64),),
stride_categories=("row_major", "transpose", "broadcast"),
)
def test_layout_invariant(dtype, shape, device, stride_category):
t = fuzz_strides_for_category(shape, dtype, stride_category, device=device)
...
New module:
- src/gpucheck/fuzzing/strides.py with fuzz_strides, fuzz_strides_for_category,
StrideStrategy, CATEGORIES.
Tests added:
- tests/test_fuzz_strides.py (17 tests covering each category and edge cases)
- tests/test_fuzz_strides_hypothesis.py (3 Hypothesis property tests)
- tests/test_parametrize_gpu_strides.py (3 wiring tests)
Test count: 117 baseline -> 139 passing (+22 net new). ruff and mypy strict
pass clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… TM-E1
Track-C of the gpucheck v1.0 release fixes the documented
"Thread-safety issue in tolerance override stack" gap (CLAUDE.md weakness)
and addresses security finding TM-E1.
Tolerance override stack:
- Replace module-level list `_tolerance_overrides` with a
`contextvars.ContextVar`. Each OS thread (and each asyncio task that
copies the current context) sees its own override stack.
- `tolerance_context(atol, rtol)` now uses ContextVar.set / .reset(token),
which is exception-safe by construction.
- The user-facing API is unchanged: `with tolerance_context(...): ...`
TM-E1 mitigation in sanitizers/race.py:
- _find_compute_sanitizer normalizes CUDA_HOME / CUDA_PATH via
os.path.realpath and validates against _CUDA_HOME_ALLOWLIST
(/usr/local/cuda, /opt/nvidia/cuda, /opt/cuda).
- A symlink pointing outside the allowlist is correctly rejected.
- A path that lookalikes a prefix (e.g. /usr/local/cuda-evil) is rejected
by exact-prefix-with-separator matching.
- Rejected paths emit a RuntimeWarning explaining the rejection.
Tests added:
- tests/test_tolerance_thread_safety.py
- test_tolerance_context_is_thread_isolated: 4-thread Barrier-coordinated
stress test; fails on the unfixed plain-list code, passes on ContextVar.
- test_tolerance_context_pop_is_correct_after_exception
- test_tolerance_context_nesting_in_single_thread
- tests/test_race_cuda_home_allowlist.py (6 tests covering the canonical
paths, lookalikes, missing PATH, allowlisted binary, symlink attack)
Test count: 117 baseline -> 126 passing (+9 net new). ruff and mypy strict
pass clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… hardening Track-D of the gpucheck v1.0 release closes four documented gaps in one shot, splits across the reporting and sanitizers packages plus CI plumbing. D.1 reporting test coverage 0 -> 98% - tests/test_reporting_console.py (10 tests) — Rich-based renderer. - tests/test_reporting_json.py (6 tests) — RunRecord schema, compare_runs classifies regression / ok / new / removed / div-by-zero. - tests/test_reporting_ci.py (8 tests) — GitHub Actions annotations, JUnit XML well-formedness, PR comment Markdown. D.2 reporting/html.py — static HTML dashboard - Self-contained HTML (no external CSS/JS, no fetches at view time). - Inline SVG bar chart for benchmark medians. - Test results / benchmarks / memory / comparison sections. - Empty data handled gracefully. - HTMLParser well-formedness verified; XSS escaping verified. D.3 sanitizers/determinism.py — assert_deterministic + @requires_determinism - Seeds random / numpy / torch (CPU+CUDA+MPS) before each invocation. - Compares torch.Tensor / tuple / list / scalar outputs byte-identically. - DeterminismError surfaces SYNTHESIS §4 best-effort caveat in failure msg. - 8 tests covering pass/fail/exception/nesting/torch-tensor paths. D.4 DEP-1 mitigation — uv.lock committed - 1220-line lock file from existing dev environment. - CI now installs via `uv sync --frozen --extra dev`. D.5 CFG-2 mitigation — .github/workflows/ci.yml - Top-level `permissions: contents: read` block. - Default GITHUB_TOKEN is no longer write-all. Test count: 117 baseline -> 157 passing (+40 net new). Reporting coverage: 0% -> 98% (target was 90%). ruff and mypy strict pass clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resolves tolerances.py conflict between Track-A (MPS multipliers + xfail registry) and Track-C (ContextVar override stack). C's ContextVar wins for `_tolerance_overrides`; A's `_MPS_TOLERANCE_MULTIPLIERS` and xfail registry stack alongside. Verified: 16 tests pass across both feature sets (test_tolerance_thread_safety + test_assert_close_mps + test_mps_xfail).
…xtra Track-D committed uv.lock generated against pre-A pyproject.toml. Phase 3 merge brings in the [mps] / [apple] extras with torch>=2.6 floor. Regen captures these for reproducible installs.
Document the four v1.0 tracks (MPS backend, stride fuzzing, thread-safe tolerances, release bundle) with citations to engineering DIFF_LOG and research SYNTHESIS. Reconciles the prior "8 real bugs" claim with the verified count (2 externally filed: triton#9838, triton#9839).
…commits Cover dev setup (uv sync --frozen), test running (224 passing, MPS auto-runs on Apple Silicon), code style (ruff + mypy strict), commit convention transition (Conventional Commits going forward; legacy '[ Type ] :' bracket history unchanged), PR process, expert system, and the engineering / docs team protocols at ~/.claude/teams/.
Documents the new Backend Protocol entry points (still backwards compatible with detect_gpus()), GPUInfo.backend field default, @Devices() MPS auto-detection, ContextVar-based tolerance overrides, [tool.gpucheck.mps.xfail] registry, the PROVISIONAL 2x MPS multiplier, new fuzz_strides API, and the committed uv.lock for reproducible installs.
…inter Update the lead paragraph to reflect MPS as first-class, add the [mps] install extra, replace the AMD ROCm 'not supported yet' caveat with explicit MPS-supported / ROCm-and-XPU-planned phrasing, and reconcile the "8 real bugs" claim with the visible table via a footnote citing the 2 externally verified upstream issues (triton#9838 open, triton#9839 closed) per research SYNTHESIS Sub-Q 8.
… bundle Move MPS, stride/contiguity fuzzing, thread-safety, HTML reporting, and determinism out of "Known Weaknesses & Gaps" — they all shipped in v1.0. Add a new "v1.0 highlights" section summarising the four engineering tracks. Update Git Conventions to formalize Conventional Commits going forward (legacy bracket-style history unchanged). Update architecture overview with the new backends/ package. Update PyPI line to v1.0.0rc1.
CI lint job (mypy strict on Linux py3.12) flagged `src/gpucheck/fuzzing/strides.py:295` unused-ignore. Local verifier during Track B did not surface this — likely because hypothesis was installed lazily after that file was written. Removing the ignore is the minimal fix; the decorator is now correctly inferred.
…-05 — IMPL_PLAN_v1.1) memory_guard() previously yielded a private _MutableReport class, leaking an underscore-prefixed type through a public API and forcing callers to depend on an unstable name. Rename to MemoryGuardReport (slots dataclass) and re-export from gpucheck.sanitizers. Avoids collision with fixtures.profiler.MemoryReport (frozen, fixture-side summary) by using a distinct name; the existing MemoryReport backward-compat alias on SanitizerMemoryReport is preserved. Refs: detector-files Top-10 #8, synthesist ISS-15, planner row 119.
… (T-01) Replace the module-level `try: import torch as _torch / _has_torch` block in `src/gpucheck/assertions/close.py` with a cached `_torch_mod()` helper that performs the import only when an API needs it. Both call sites inside `assert_close` (device-type detection and the GPU fast-path) bind `_torch = _torch_mod()` at the top of the function and guard with `_torch is not None`. Verified via `python -c "import gpucheck.assertions.close; import sys; assert 'torch' not in sys.modules"`. All 32 tests in test_assertions.py remain green. Source: detector-files Top-3 #1; synthesist ISS-08; planner T-01. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ontract (T-04 — detector-files #3) Replaces 7 bare `except Exception:` sites with narrow tuples + debug-level log messages so silent fallbacks can be diagnosed: backends/mps.py L99 torch.mps.device_count() -> (RuntimeError, AttributeError) L137 current_allocated_memory() -> (RuntimeError, AttributeError) L141 driver_allocated_memory() -> (RuntimeError, AttributeError) L148 recommended_max_memory() -> (RuntimeError, AttributeError) L191 recommended_max_memory() -> (RuntimeError, AttributeError) arch/detection.py L157 pynvml CUDA driver version -> (pynvml.NVMLError, AttributeError) L229 torch.cuda.mem_get_info() -> (RuntimeError, AttributeError) All sites now log at DEBUG before returning the documented fallback so "why is recommended_max_memory zero on this build?" is answerable. Acceptance: ruff check src/ tests/ -> All checks passed mypy src/ -> Success: no issues found in 41 source files pytest -q -> 224 passed, 1 skipped (unchanged baseline)
…ed (T-07 — IMPL_PLAN_v1.1) The README claimed `pytest tests/gpu_integration/` auto-skips without a GPU, but on MPS hosts the suite collected and failed 52 tests on CUDA-specific calls (docs-tester finding R-B21 / T-B5). Add a `pytest_collection_modifyitems` hook in `tests/gpu_integration/conftest.py` that skips every collected item unless: - CUDA is available, or - MPS is available *and* `--mps-integration` was passed. Add the `--mps-integration` flag for opt-in MPS execution. The default top-level `pytest -q` already ignores this directory via `pyproject.toml` addopts; this hook honors the README claim when the directory is invoked explicitly.
…failure (T-08 — IMPL_PLAN_v1.1) `_load_pyproject_config` previously caught bare `Exception` and silently swallowed it, masking real misconfiguration (security finding PM-2). Narrow to `(OSError, tomllib.TOMLDecodeError)` and emit a `UserWarning` so users see *why* their tolerance overrides were ignored. Programmer errors (AttributeError, TypeError, etc.) now propagate as expected. The absent-file branch still returns silently — that is the documented fall-through path to built-in defaults. Refs: security-postmerge PM-2, planner T-08.
…(T-02 — PM-4)
Insert `.contiguous()` between `.cpu()` and `.numpy()` on both torch.Tensor
branches of `_to_numpy` (the primary `hasattr(tensor, "detach")` branch and
the dlpack fallback inside `__cuda_array_interface__`). torch <2.1 raises
RuntimeError ("input array is not C-contiguous") when `.numpy()` is called
on stride-fuzzed / sliced / transposed tensors. Preventive on newer torch —
known to fire on older PyTorch.
Add `tests/test_assert_close_contiguous.py` with 6 parametrized cases
(slice / transpose / broadcast × `_to_numpy` direct / `assert_close`
end-to-end). Test count 224 → 230 passed, 1 skipped.
Source: security-postmerge PM-4; planner T-02; synthesist ISS-18.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ector-files #1, partial) Two parallel detection stacks lived at: - arch/detection.py:133,206 (pynvml + torch -> list[GPUInfo]) - fixtures/gpu.py:43,90 (pynvml + torch -> GPUDevice) Drift risk: the two stacks had different fallbacks (NVMLError vs RuntimeError vs AssertionError catches) and different "no backend" behavior (one warned, the other silently returned None). Now there is exactly one detection codepath: arch/detection._detect_gpus_or_warn() -> list[GPUInfo] | None `-- arch/detection.detect_gpus() (lru_cache wrapper, public API, maps None -> []) `-- fixtures/gpu.detect_gpu() (calls detect_gpus(), adapts the first GPUInfo to the smaller GPUDevice via _to_device()) Public API preserved: - arch.detect_gpus() still returns list[GPUInfo] (empty list on no backend; was already that way). - fixtures.gpu.detect_gpu() still returns GPUDevice | None. - The "no detection backend available" UserWarning is preserved (and is now lru_cache-deduped, firing at most once per session via detect_gpus()). The plugin.py shim (`_lazy_detect_gpus` / `_gpu_available` / `_gpu_count` at plugin.py:10-22) is owned by another agent per task instructions and left untouched; the helper this commit introduces is the foundation that agent's plugin.py refactor can call into. Removed: - fixtures/gpu._detect_gpu_pynvml (~45 LOC) - fixtures/gpu._detect_gpu_torch (~25 LOC) Acceptance: ruff check src/gpucheck/arch/ src/gpucheck/fixtures/gpu.py -> clean mypy src/ -> clean pytest -q -> 230 passed, 1 skipped (unchanged)
…(T-21 — api-dx #2) api-dx-grade weak API #2 flagged @require_arch (singular) as inconsistent with @requires_determinism (plural). Both are decorators of the same shape but spelled differently — a typo footgun that silently skips tests instead of erroring. This commit: - Adds @requires_arch as the canonical (plural) form. Same semantics as @require_arch, same alias expansion ("Blackwell" -> {"blackwell-dc", "blackwell-consumer"}), same skip-with-detected-arch error string. - Keeps @require_arch as a backward-compatible wrapper that emits a DeprecationWarning on use; will be removed in v1.2. - Re-exports both names from gpucheck.arch (and arch.compatibility). - Documents the addition + deprecation in CHANGELOG.md under [Unreleased]. The existing test suite continues to use @require_arch (verified by grep); those tests pass unchanged and surface the new DeprecationWarning, which mypy/ruff/pytest collect as informational — no test fails. Acceptance: ruff check src/ -> All checks passed mypy src/ -> Success: no issues found in 41 source files pytest -q -> 233 passed, 1 skipped (no regression) pytest -W default tests/test_arch.py -> @require_arch sites now emit DeprecationWarning("@require_arch (singular) is deprecated and will be removed in v1.2; use @requires_arch (plural) for naming consistency with @requires_determinism.")
… ~30 mutants) Add `TestMismatchReportPinnedNumerics` to tests/test_assertions.py with three new tests that lock down exact numeric values in `format_mismatch_report` output: 1. `test_max_abs_error_value_is_pinned` — pins max abs error (4.5) and mean abs error (2.5) values, plus row labels. 2. `test_mismatch_count_and_location_are_pinned` — pins `5 / 6 (83.33%)` count/total/percentage and 2-D max-error location `(1, 2)` (forces unravel_index axis mutations to fail). 3. `test_histogram_present_with_pinned_bucket_and_count` — pins histogram bucket label `[1e-3, 1e-2)` and count 3 (ANSI-stripped). Targets `assertions/reporting.py` mutator survivors documented in `EVIDENCE/mutator-survivors.md` top-leverage #1 (~30 surviving mutants). No source changes to reporting.py — tests-only per planner T-10. Test count 230 → 233 passed, 1 skipped. ruff: clean. mypy: clean. Source: planner T-10; mutator-survivors top-leverage #1; synthesist ISS-25. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…roup Final summary: 233 passed, 1 skipped; mutmut baseline 169 (unchanged); ruff + mypy clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-N1 — review BLOCKER)
External review N1: the `baseline_2x=True` branch in `assert_close` was
deriving its own k_dim scaler via `sqrt(k_dim)` while every other code
path (and the canonical `compute_tolerance` helper) uses
`sqrt(max(k_dim, 1) / 128)`. At k_dim=4096 the two diverge by
`sqrt(128) ≈ 11.3×`, silently making `baseline_2x=True` ~11× more
permissive than the un-doubled call. The headline FlashAttention
example (`assert_close(out, ref, k_dim=4096, baseline_2x=True)`) hit
this immediately — silent-correctness regression in the most-prominent
public assertion API.
Fix: drop the open-coded scaling block in `close.py:172-183` and route
through `compute_tolerance(dtype, k_dim=..., device_type=...)` for the
canonical scale, then multiply atol/rtol by 2 only if `baseline_2x` is
set. Identical semantics to the non-`baseline_2x` path; just doubled.
Regression test: `TestBaseline2xKDimScaling` in
`tests/test_assertions.py` — parametrized over k_dim ∈ {128, 1024, 4096}.
Pins the contract that `baseline_2x` is exactly 2× canonical, NOT
`2 × sqrt(128)`× canonical. Tests both the pass boundary
(diff = 1.5× canonical) and the fail boundary (diff = 2.5× canonical).
Verification: 233 → 239 tests passing; ruff + mypy clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…view BLOCKER) External review A1: the canonical category constants in `src/gpucheck/fuzzing/strides.py` use snake_case (`row_major`, `broadcast`, `non_contig`, ...), but `MIGRATION.md` §7 examples named them in kebab-case (`row-major`, `broadcast-induced`, `contiguous-after-clone`, `gather-induced`). Every copy-pasted MIGRATION snippet hit `ValueError: Unknown stride category 'broadcast-induced'`. Fix option (a) chosen — accept BOTH forms at the API boundary and normalize to the canonical snake_case internally; deprecation-warn on non-canonical input. Rationale: - Real users have already copy-pasted the kebab examples into their codebases. Hard-failing them on upgrade is a worse first-impression than carrying a one-tier alias table. - `_canonicalize_category()` is a pure-string lookup; zero runtime cost on the canonical path. - DeprecationWarning gives users a discoverable path off the alias. - The public `CATEGORIES` constant remains snake_case (Python convention; matches the in-tree code style). Also rewrites `MIGRATION.md` §7 to use the canonical snake_case names and adds a note that kebab forms are deprecated aliases. Drops the nonsense `n=20` from the fuzz_strides example (corollary of D4). Test: `tests/test_fuzz_strides.py::test_fuzz_strides_for_category_accepts_kebab_alias` parametrizes 7 alias→canonical pairs; asserts both spellings produce the same tensor (same seed) and that DeprecationWarning fires for the kebab form. A second test exercises `fuzz_strides(categories=...)` with mixed kebab inputs. Verification: 239 → 247 tests passing; ruff + mypy clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…(B-S1 — review BLOCKER) External review S1: `src/gpucheck/plugin.py:_load_pyproject_config` falls back to `import tomli as tomllib` on Python 3.10 (which lacks `tomllib` in the stdlib). But `tomli` was NOT declared in `[project.dependencies]` of `pyproject.toml`. On a fresh `pip install gpucheck` on 3.10 with no transitive coverage of `tomli`, the import fails — and the previously-broad `except` (now narrowed by T-08) silently no-oped the user's `[tool.gpucheck.tolerances]` and `[tool.gpucheck.mps.xfail]` overlays. Confidence-in-test failure mode (OWASP-A05 configuration trust). Fix: add `'tomli>=2.0; python_version < "3.11"'` to `[project.dependencies]` using PEP 508 marker syntax. The marker ensures Python 3.11+ does NOT pull tomli (stdlib `tomllib` is preferred there). Test: `tests/test_plugin_tomli.py::test_tomli_is_importable_on_python_310` asserts `tomli` is importable on the 3.10 path (skipped on 3.11+). A second smoke test exercises the loader on whichever TOML backend the running interpreter resolves to, pinning that the `[tool.gpucheck.tolerances]` overlay actually applies end-to-end. Verification: 247 → 248 passed, 1 → 2 skipped (the 3.10-only test correctly skips on the 3.12 runner); ruff + mypy clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…t MIGRATION example (B-A2 — review BLOCKER)
External review A2: two cooperating defects in the determinism API.
(1) MIGRATION.md §8 example invoked `assert_deterministic` with three
kwargs that don't exist: `args=`, `runs=`, `atol=`. The real
signature is `assert_deterministic(fn, *args, n=3, seed=0,
**kwargs)`. The bogus kwargs would forward into the user's kernel
via `**kwargs`, crashing on the first call.
(2) The function compared outputs via `torch.equal` (bit-exact) with
no tolerance knob. Research SYNTHESIS §4 documents that MPS is
*best-effort* deterministic — pytorch#181936 / #170837 / #177116
show real run-to-run drift on Apple Silicon. Byte-equality is the
wrong default contract for the headline MPS use case the docs
advertise.
Fix:
- Add `atol: float = 0.0` and `rtol: float = 0.0` parameters to
`assert_deterministic`. When both are zero (the back-compat
default), comparison stays bit-exact (`torch.equal`). When either
is non-zero, comparison switches to `torch.allclose(..., atol, rtol)` —
the right contract for MPS.
- Forward atol/rtol through `requires_determinism` so the decorator
form gets the same knob.
- Updated the failure message to identify which mode raised
(`mode=byte-identical` vs `mode=allclose(atol=..., rtol=...)`) so
CI logs are self-explanatory.
- Rewrote MIGRATION.md §8 example to use the real signature
(positional `*args`, `n=`, optional `atol=`/`rtol=`).
Tests (5 new):
- byte-identical default rejects 1-ULP drift
- atol > drift => pass
- atol < drift => DeterminismError with `allclose` in the message
- rtol path covered separately
- decorator forwards atol correctly
Verification: 248 → 253 passed, 2 skipped; ruff + mypy clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`uv` re-resolved the lockfile when `tomli; python_version < "3.11"` was added to `[project.dependencies]` in commit 0da3fd4 (B-S1). The single line added to uv.lock is the marker-gated tomli inclusion under the gpucheck `dependencies` and `requires-dist` sections. No code change; lockfile-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ctor 5K-sample calibration on M5 (drift_histogram_5k.json) confirmed the 2× starting multiplier within ±6% for matmul; flagged conv2d as an outlier (+225%/+75%/+70% over v3 projection). The per-(kernel, dtype) refactor required to address conv2d ships in v1.1 as task T-24, not v1.0. Comment-only — no behavior change. PROVISIONAL flag intact.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
gpucheck v1.0.0rc1 — first major release. Four parallel engineering tracks delivered:
feat/track-a-mps):gpucheck.backends.{Backend, CUDABackend, MPSBackend},@devices("mps"), deadlock-safe MPS event timing (avoids pytorch#162872), 2× MPS tolerance overlay (PROVISIONAL — pending P99 calibration), 12-entry[tool.gpucheck.mps.xfail],[mps]/[apple]install extras (torch>=2.6).feat/track-b-strides):fuzz_strides()deterministic 7-category corpus,StrideStrategyfor Hypothesis,parametrize_gpu(stride_categories=...)wiring.feat/track-c-thread-safety):tolerance_context()backed bycontextvars.ContextVar(safe forpytest-xdist, threads, asyncio). Mitigates security finding TM-E1 (path-injection viaCUDA_HOME/CUDA_PATH).feat/track-d-bundle): HTML dashboard reporter,assert_deterministic/@requires_determinism, committeduv.lock(DEP-1), CI permissions hardening (CFG-2), reporting test coverage 0% → 98%.Test count: 117 → 224 (+107). All ruff E/F/W/I/N/UP/B/A/SIM/TCH + mypy strict clean.
Dogfooding: Real MPS run on this Mac at
dist/dashboard-v1.0.html— matmul fp32 throughput 63 → 1133 → 1703 GFLOPs at 256/1024/2048 with the deadlock-safe event timer.Bug-finding swarm: 26 kernels × 250 iterations = 6 500 iterations on real MPS. UPSTREAM.md verdict: 0 issues filed — all swarm-flagged divergences are tolerance-recalibration territory (1-5× atol), not upstream PyTorch bugs. The PROVISIONAL 2× multiplier needs P99 calibration on the M-machine; conv2d
stride=slicefp16/bf16 is a candidate xfail entry.Team protocol: session ran on claude-forge — see
.claude/teams/{research,engineering,security,testing,docs,forge}/v1.0/for the full evidence trail (SYNTHESIS.md, FINDINGS.md, DIFF_LOG.md, swarm/RESULTS_*.md, evaluator.md per team).Test plan
uv run pytest -q→ 224 passed, 1 skipped (was 117)cuda0test, no NVIDIA hardware)dist/dashboard-v1.0.html, real benchmarks)assert_deterministic) testedThreadPoolExecutor~/.pypircnot configured locally)🤖 Generated with Claude Code