Repository navigation
test: verification hardening and test reliability - #10
Conversation
…ty tests Audit of timing-sensitive tests (lifecycle, reliability, live server, streaming, errors, load-test, observability, priority). Changed only where fragility was demonstrated; behavioral assertions are unchanged. - test_lifecycle mid-partial cancellation: a fixed 0.4 s sleep after a 0.3 s decode asserted the slot was released (~100 ms slack, the macOS flake class fixed in PR #9). With the worker thread returning 0.25 s late the old test fails (assert 1 == 0); it now waits on state with gated.until() and always shuts the runner down. - test_reliability slow sender: frames every 0.35 s against a 0.5 s idle limit left 150 ms of margin; one late client wake-up fires idle_timeout. Now 0.1 s of audio every 0.15 s: still slower than real time (1.5x), 0.35 s margin. Passes with a 0.2 s late wake-up injected; still fails if the idle timer stops resetting per frame (whole stream > 0.5 s). - Five unbounded `while ...: sleep` polls (lifecycle, and the reliability final-decode disconnect, queued disconnect, malformed-message and shutdown tests) now use bounded condition waits: a regression that prevents the awaited state fails in ~5 s with a message instead of hanging until the CI job timeout (demonstrated by disabling partial scheduling: old versions hang, new fail with "timed out waiting for partial decode to start"). Left as is (already bounded, deliberate, or wide margins): live-server polls, idle/send/load-test wall-clock bounds (2-3 s for 0.3-0.5 s operations), the event-loop-lag test, gate pumps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DfLG9q5h7eiawGNwaBswq
Recorder (benchmarks/whisper_verification.py): a `provenance` section that never changes `complete`, separating what the environment said from what the test process loaded: - environment_metadata: allowlisted CUDA / loader / pytest variables as passed to pytest (LD_LIBRARY_PATH as entries, LD_PRELOAD, CUDA_*, NVIDIA_*, CT2_*, PYTEST_DISABLE_PLUGIN_AUTOLOAD), which search-path directories hold libcublas.so.12 / libcublasLt.so.12 / libcudnn.so.9, and installed nvidia-* wheels. Nothing outside the allowlist is read; URL userinfo and secret-looking KEY=value entries are redacted before path lists are split. Labelled as metadata, not proof of loading. - pytest_plugins: pytest11 entry points with distribution versions, and the plugins line pytest printed for the run. - loaded_libraries: CUDA / CTranslate2 shared objects mapped into the pytest process after the real-speech decodes (/proc/self/maps), as recorded by that test. PYTEST_PLUGINS is now treated as an override like PYTEST_ADDOPTS and PYTHONPATH (it loads arbitrary plugin modules): a stricter check, no existing requirement relaxed. tests/test_whisper.py: the real-speech test records the loaded CUDA libraries; the 0.0708 baseline comment is corrected (Phase 1 scored the same transcripts with the v1 normalizer; the v2 value the tests compute is 0.0531). WER bounds unchanged. Offline tests for every new recorder behaviour (allowlist, redaction, search-path detection, wheels, plugin inventory and header parsing, loaded-library parsing, provenance not affecting completeness). Docs: the T4 result stays tied to 096a034; the changed recorder and test file are marked as not yet re-verified on a GPU. Provenance fields, their limits, and how to reproduce the verified environment are documented. PR #9's raw evidence is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DfLG9q5h7eiawGNwaBswq
Final Engineering Review — PR #10Review status: Conditionally approved, pending NVIDIA T4 re-verification. Reviewed the verification-hardening changes at What looks good
Final verification requiredBefore merging, rerun the eight real-model tests on an NVIDIA T4 using the exact PR head ( Confirm:
Preserve the new verification output separately from PR #9's historical evidence. Pytest plugin policyDecision: Defer plugin enforcement to a separate follow-up. Keep the current inventory-only approach for PR #10. For future verification runs, prefer evaluating This requires separate validation because it changes the test environment and potentially the definition of a complete run. Merge decisionNo blocking issues identified in the reviewed changes beyond the outstanding real-GPU verification. Once the T4 run passes and its provenance is inspected, PR #10 is cleared for squash merge. Do not merge until that verification is complete. |
F1 Unreadable library-path directories no longer crash the recorder:
Path.exists/is_dir raised PermissionError under an unreadable parent
(reproduced as a non-root user on 3.11 and 3.13), aborting record.json
after the tests ran. Each probe now records the error
(ld_library_path_entries_exist, search_errors). Each provenance section
is collected independently; one that raises is recorded as
collection_error and fails the run closed, instead of losing the record.
F2 /proc/self/maps: the pathname is everything after the fifth field, so
paths with spaces ("My Drive") are kept whole (was: last token only);
" (deleted)" is dropped. The parser moved to the recorder module and is
tested offline, including against a real mapping in this process.
F3 Plugin inventory renamed and documented accurately:
installed_entry_points (what pytest can auto-load) and header_reported
(pytest's plugins: line, entry-point plugins only). Neither claims to
list every plugin that ran: -p, PYTEST_PLUGINS, conftest.py and
pytest_plugins routes are covered by the override and checkout rules.
F4 Redaction: URL userinfo is removed up to the last "@" (passwords with
"@" or "/" previously leaked, in part or whole); secret-like KEY=value
assignments are redacted anywhere, including query strings; common token
formats (GitHub, Hugging Face, AWS, sk-, Slack, GitLab, JWT) are
redacted. Documented as best effort: the allowlist remains the main
control and unknown formats are not caught.
F5 Checkout audit (fail closed), taken before pytest starts: any untracked
non-ignored file anywhere, or any ignored *.py/*.pyc/*.pyo/*.pth/*.so or
pytest/Python config file directly in the repo root, tests/ or src/,
makes the run incomplete; an audit failure does too. git status
--untracked-files=no (the dirty check) let an untracked root conftest.py
run inside the tests while reporting a clean checkout; reproduced, and
now reported as "untracked files present (1): ['conftest.py']".
F6 LD_PRELOAD is split on spaces and colons, mixed; the lifecycle test
cancels its session task if a bounded wait fails.
Existing requirements (exact 8 tests, model identity, WER bounds 0.12 /
0.25, clean commit, overrides) are unchanged. No production code,
dependency, CI or raw-evidence change. Not yet re-verified on a GPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DfLG9q5h7eiawGNwaBswq
Final Verification — NVIDIA T4 / CUDA FP16PR #10 verification is complete. The final run was executed in a Google Colab NVIDIA Tesla T4 environment against the exact PR head: Commit: Verification Results
Speech Evaluation
These are workload-specific measurements, not claims of improved decoding accuracy or broader production performance. Environment Issue and ResolutionThe initial Colab run failed because CTranslate2 could not resolve No test thresholds, application logic, or historical benchmark evidence were modified to obtain the passing result. Final AssessmentThe PR #10 verification-hardening changes have passed the available CI checks and the final real-GPU verification. The evidence archive, Recommendation: Approved for squash merge, provided the PR head remains at the verified commit and required CI checks remain green. |
Review fixes F1–F6 (
cac3739)These came from the independent review of
1380606. Each was reproduced first, then fixed with regression tests, then re-run against the original fault.LD_LIBRARY_PATHentry madePath.exists/is_dirraisePermissionError, abortingrecord.jsonafter the tests ranld_library_path_entries_exist,search_errors). Each provenance section is collected independently; one that raises is recorded ascollection_errorand fails the run closed./proc/self/mapspaths with spaces were truncated (Drive/libcublas.so.12)(deleted)dropped; parser shared and tested offline…/My Drive/…is reported with its full path-p,PYTEST_PLUGINS,conftest.pyandpytest_pluginspluginsinstalled_entry_points/header_reported, with a note; docs say these routes are covered by the override and checkout rules, not the inventoryconftest.pyis absent fromheader_reported, as documented@leaked a fragment; one containing/leaked whole; bare tokens weren't redacted@; secret-likeKEY=valueredacted anywhere, including query strings; GitHub / HF / AWS /sk-/ Slack / GitLab / JWT formats redacted. Documented as best effort.conftest.pyran inside the tests withdirty: False*.py/*.pyc/*.pyo/*.pth/*.soor pytest/Python config file in the repo root,tests/orsrc/, makes the run incomplete; so does an audit failureconftest.py, with no overrides. All 8 tests "passed"; the recorder reporteduntracked files present (1): ['conftest.py']LD_PRELOADmixed separators; the lifecycle test leaked its task on failurefinallycancels and awaits the taskUnchanged: the existing requirements (exactly 8 tests, model identity, WER bounds 0.12 / 0.25, clean commit, overrides),
src/, dependencies, CI and raw evidence.Problem
LD_LIBRARY_PATHfix (cuBLAS 12) that the record could not capture. pytest auto-loaded plugins from the Colab image that the record listed only implicitly, inpytest.log. Nothing in the record showed which CUDA libraries were actually loaded.Changes
1. Test reliability (
d8b24e2)I audited the timing-sensitive tests in
test_lifecycle.py,test_reliability.py,test_live_server.py,test_streaming.py,test_errors.py,test_load_test_timeout.py,test_observability.pyandtest_priority.py. Tests changed only where fragility was demonstrated, and every behavioural assertion is kept.test_lifecycle::…mid_partial_cancels_receive_and_partialsleep(0.4)after a 0.3 s decode, then asserts the slot was released: ~100 ms of slack (the PR #9 flake class). Also an unboundedwhile in_flight == 0poll.until()for both; runner shut down infinallytest_reliability::test_slow_sender_within_idle_timeout_completestest_reliabilityfinal-decode disconnect, queued disconnect, malformed message, shutdownwhile …: sleeppolls: a regression hangs until the CI job timeout_await_metrics(): bounded wait that fails with a messageShown to detect what they're meant to catch. Each old test (from
main) and new test ran under the same injected condition:main)assert 1 == 0onin_flight)idle_timeout)idle_timeout)Left as is:
test_inference_does_not_block_event_loop(its< 0.5 sbound during a 1 s decode is the property under test, with 2× headroom)2. Verification provenance (
1380606, revised incac3739)The record gains a
provenancesection. It never changescomplete. It separates what the environment said from what the test process loaded:environment_metadata: what the environment said. Explicitly labelled as not proof of loading.LD_LIBRARY_PATH(as entries),LD_PRELOAD,CUDA_HOME,CUDA_PATH,CUDA_VISIBLE_DEVICES,CUDA_MODULE_LOADING,NVIDIA_VISIBLE_DEVICES,NVIDIA_DRIVER_CAPABILITIES,PYTEST_DISABLE_PLUGIN_AUTOLOAD, and anyCT2_*. Nothing outside the allowlist is read.LD_LIBRARY_PATHdirectories holdlibcublas.so.12,libcublasLt.so.12andlibcudnn.so.9.nvidia-*pip distributions and their versions.KEY=valueentries are removed before path lists are split.pytest_plugins:installed_entry_points(pytest11entry points with distribution and version: what pytest can auto-load) andheader_reported(theplugins:line pytest printed). Neither lists every plugin that ran. Plugins loaded via-p,PYTEST_PLUGINS,conftest.pyorpytest_pluginsdon't appear in either; those routes are covered by the override and checkout rules instead.loaded_libraries: what was actually loaded. CUDA / CTranslate2 shared objects mapped into the pytest process after the real-speech decodes, read from/proc/self/maps(Linux). The real-speech test records this, before its assertions.Stricter, not looser.
PYTEST_PLUGINSnow counts as an override, likePYTEST_ADDOPTSandPYTHONPATH, because it loads arbitrary plugin modules. No existing requirement is relaxed: the model-identity, exact-8-tests, WER and clean-checkout rules are unchanged.Defect found and fixed during this work. My first redaction split
LD_LIBRARY_PATH-style lists on:before redacting. That cut a URL apart, so the remaininguser:pw@piece was no longer recognised as a URL and would have been recorded. Redaction now runs on the whole value first. A regression test asserts that no fragment of the credentials survives.Offline tests (
tests/test_whisper_verification.py, +9) cover:PYTEST_PLUGINSas an overrideEnd-to-end dry run. With the stub model from PR #9, the recorder:
NOT COMPLETE, naming the stub classes and the injected overrides3. Documentation
tests/test_whisper.py: the 0.0708 comment now explains the v1/v2 distinction. The v2 corpus value the tests compute is 0.0531; Phase 1's 0.0708 is the same transcripts scored with v1. The bounds (0.12 and 0.25) are unchanged.docs/whisper-verification.md:docs/benchmarks/whisper-t4/README.md(narrative, not raw evidence): no longer claims the test file and recorder are unchanged since096a034; states that the result applies to096a034.README.md: the T4 statement names096a034and notes the offline-only status of later changes.Guardrails
src/(inference, scheduling, WebSocket protocol, model behaviour), WER thresholds, all raw benchmark artifacts includingdocs/benchmarks/whisper-t4/raw/, dependencies, and CI. Confirmed withgit diff --quiet origin/main.Test results (exact)
Local, Linux, no GPU:
python -m pytest -q -m "not whisper"× all 4 flag combinations (F1–F6 code, before two cosmetic lint edits)cac3739python -m pytest -m whisper --collect-only -qpython -m benchmarks.verify_evidencegit diff --checkruff --select F,E9on changed filesnumpyunused intest_reliability.py, already onmain)307 = 269 on
main+ 38 new offline tests (9 provenance + 29 for F1–F6). The repo has no configured lint or type checker. With extended ruff rules, this PR adds no findings beyond those already onmain; one deliberate broadexcept(provenance collection must never abort the record) is annotated.CI (GitHub Actions, separate from the local runs): run 40214206943 at
cac3739, 10/10 passed on the first attempt, including macOS. The previous head1380606also passed 10/10 (run 40207706030).Real-model tests: not run. There's no GPU here and Hugging Face is unreachable. GPU verification at
cac3739is pending: the Colab cells are prepared, and the run must not use1380606.Remaining limitations
tests/test_whisper.pyhave not been run against the real model./proc/self/mapsshows for the pip CUDA wheels.docs/whisper-verification.mdat this commit would verify it and produce the first record with provenance.LD_PRELOADor an unexpected entry-point plugin does not make a run incomplete; only failing to collect provenance does.~/.config) are not audited; they are covered only by the identity checks and by running in a fresh runtime.loaded_librariesis only as trustworthy as the test code. It comes from the tests' own process. The existing integrity rules (no overrides, clean checkout, exact test set) are what protect it.LD_LIBRARY_PATHonly, notRUNPATH,ld.so.cacheor system directories, which is why the actually-loaded list exists.Pytest plugin enforcement: decided, inventory only
Per review, this PR keeps the inventory-only approach. It records the installed and reported plugins without allowlisting them or disabling auto-loading. Plugin enforcement may be evaluated separately in a future change; options include an allowlist, or
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 -p asyncio.https://claude.ai/code/session_017DfLG9q5h7eiawGNwaBswq
Generated by Claude Code