From c9a996912e73c2a5aa0046f8afec5518c892b2e6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:00:26 +0900 Subject: [PATCH 01/19] test(analysis): expose blanket deprecation suppression --- .../tests/test_deprecation_warning_policy.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 services/analysis-engine/tests/test_deprecation_warning_policy.py diff --git a/services/analysis-engine/tests/test_deprecation_warning_policy.py b/services/analysis-engine/tests/test_deprecation_warning_policy.py new file mode 100644 index 000000000..a7c66c01c --- /dev/null +++ b/services/analysis-engine/tests/test_deprecation_warning_policy.py @@ -0,0 +1,15 @@ +"""Tests for analysis-engine deprecation-warning visibility policy.""" + +from __future__ import annotations + +import tomllib +from pathlib import Path + + +def test_pytest_does_not_hide_all_deprecation_warnings() -> None: + """Require pytest to surface unowned deprecations instead of ignoring them globally.""" + pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml" + config = tomllib.loads(pyproject_path.read_text(encoding="utf-8")) + filters = config["tool"]["pytest"]["ini_options"].get("filterwarnings", []) + + assert "ignore::DeprecationWarning" not in filters From 5093dce9425d94b5dc38b54273e635a42d0648aa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:00:43 +0900 Subject: [PATCH 02/19] fix(analysis): fail tests on unowned deprecations --- services/analysis-engine/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/analysis-engine/pyproject.toml b/services/analysis-engine/pyproject.toml index fb8f7f062..49bbd1e9e 100644 --- a/services/analysis-engine/pyproject.toml +++ b/services/analysis-engine/pyproject.toml @@ -33,7 +33,7 @@ packages = ["src/bandscope_analysis"] testpaths = ["tests"] pythonpath = ["src"] filterwarnings = [ - "ignore::DeprecationWarning", + "error::DeprecationWarning", ] [tool.coverage.run] @@ -55,4 +55,4 @@ ignore_missing_imports = true line-length = 100 [tool.ruff.lint] -select = ["E", "F", "I", "B"] +select = ["E", "F", "I", "B"] \ No newline at end of file From 7885f89c59d3e4b2296efc2fee0379bb09af33f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:02:42 +0900 Subject: [PATCH 03/19] test(analysis): reject broad audioread deprecation filters --- .../tests/test_deprecation_warning_policy.py | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_deprecation_warning_policy.py b/services/analysis-engine/tests/test_deprecation_warning_policy.py index a7c66c01c..9259a1bcd 100644 --- a/services/analysis-engine/tests/test_deprecation_warning_policy.py +++ b/services/analysis-engine/tests/test_deprecation_warning_policy.py @@ -2,14 +2,58 @@ from __future__ import annotations +import ast import tomllib from pathlib import Path +_ANALYSIS_ROOT = Path(__file__).resolve().parents[1] +_AUDIO_LOADER_PATHS = ( + _ANALYSIS_ROOT / "src/bandscope_analysis/temporal/analyzer.py", + _ANALYSIS_ROOT / "src/bandscope_analysis/transcription/api.py", + _ANALYSIS_ROOT / "src/bandscope_analysis/separation/audio_separator.py", +) -def test_pytest_does_not_hide_all_deprecation_warnings() -> None: - """Require pytest to surface unowned deprecations instead of ignoring them globally.""" - pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml" + +def _is_blanket_audioread_deprecation_filter(call: ast.Call) -> bool: + """Return whether one call hides every audioread ``DeprecationWarning``.""" + if not isinstance(call.func, ast.Attribute) or call.func.attr != "filterwarnings": + return False + if not call.args or not isinstance(call.args[0], ast.Constant): + return False + if call.args[0].value != "ignore": + return False + + keywords = {keyword.arg: keyword.value for keyword in call.keywords if keyword.arg} + category = keywords.get("category") + module = keywords.get("module") + return ( + isinstance(category, ast.Name) + and category.id == "DeprecationWarning" + and isinstance(module, ast.Constant) + and module.value == "^audioread" + ) + + +def test_pytest_fails_on_unowned_deprecation_warnings() -> None: + """Require pytest to turn an unowned deprecation into a test failure.""" + pyproject_path = _ANALYSIS_ROOT / "pyproject.toml" config = tomllib.loads(pyproject_path.read_text(encoding="utf-8")) filters = config["tool"]["pytest"]["ini_options"].get("filterwarnings", []) + assert "error::DeprecationWarning" in filters assert "ignore::DeprecationWarning" not in filters + + +def test_audio_loaders_do_not_blanket_hide_audioread_deprecations() -> None: + """Keep audio loaders from suppressing every audioread deprecation at runtime.""" + offenders: list[str] = [] + for path in _AUDIO_LOADER_PATHS: + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + if any( + _is_blanket_audioread_deprecation_filter(node) + for node in ast.walk(tree) + if isinstance(node, ast.Call) + ): + offenders.append(str(path.relative_to(_ANALYSIS_ROOT))) + + assert offenders == [] From ff57338040adfd42404c8066f2ac932aa3e94e82 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:03:00 +0900 Subject: [PATCH 04/19] fix(analysis): stop hiding audioread deprecations in temporal decode --- .../src/bandscope_analysis/temporal/analyzer.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py index 7fe5ae6f7..6020c9cf6 100644 --- a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py +++ b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py @@ -85,9 +85,6 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: ) with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", category=DeprecationWarning, module=r"^audioread" - ) warnings.filterwarnings("ignore", category=FutureWarning, module=r"^audioread") # Keep the loader's known third-party churn quiet without hiding From 594ea6de4ebb776e8cd7d41299339299a90d7f1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:03:16 +0900 Subject: [PATCH 05/19] fix(analysis): stop hiding audioread deprecations in transcription --- .../src/bandscope_analysis/transcription/api.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/transcription/api.py b/services/analysis-engine/src/bandscope_analysis/transcription/api.py index f2a732d31..057be226c 100644 --- a/services/analysis-engine/src/bandscope_analysis/transcription/api.py +++ b/services/analysis-engine/src/bandscope_analysis/transcription/api.py @@ -3,7 +3,6 @@ from __future__ import annotations import io -import warnings from dataclasses import dataclass import librosa @@ -42,14 +41,12 @@ def transcribe_bass_stem(stem_data: bytes) -> list[NoteEvent]: if len(stem_data) > MAX_STEM_BYTES: raise ValueError("Stem data is too large for transcription.") - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"^audioread") - y, sr = librosa.load( - io.BytesIO(stem_data), - sr=TARGET_SR, - mono=True, - duration=MAX_TRANSCRIPTION_DURATION_SECONDS, - ) + y, sr = librosa.load( + io.BytesIO(stem_data), + sr=TARGET_SR, + mono=True, + duration=MAX_TRANSCRIPTION_DURATION_SECONDS, + ) y_array = np.asarray(y, dtype=np.float32) if y_array.size == 0 or float(np.max(np.abs(y_array))) < MIN_SIGNAL_PEAK: From 434baa1bd3e27b8a49d8751334a27ffacb51f830 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:03:35 +0900 Subject: [PATCH 06/19] fix(analysis): stop hiding audioread deprecations in separation --- .../src/bandscope_analysis/separation/audio_separator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py index c36e0f1fc..cb3331a5e 100644 --- a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py +++ b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py @@ -201,9 +201,6 @@ def _load_audio(self, path: Path) -> tuple[AudioStemArray, int]: ) with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", category=DeprecationWarning, module=r"^audioread" - ) warnings.filterwarnings("ignore", category=FutureWarning, module=r"^audioread") for category, message, module in KNOWN_LIBROSA_NUMBA_WARNING_FILTERS: warnings.filterwarnings( From 0364d68200a9822f3df2d722164ab3f1a6d89c07 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:03:52 +0900 Subject: [PATCH 07/19] chore(analysis): preserve pyproject final newline --- services/analysis-engine/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/analysis-engine/pyproject.toml b/services/analysis-engine/pyproject.toml index 49bbd1e9e..ca28f2e9d 100644 --- a/services/analysis-engine/pyproject.toml +++ b/services/analysis-engine/pyproject.toml @@ -55,4 +55,4 @@ ignore_missing_imports = true line-length = 100 [tool.ruff.lint] -select = ["E", "F", "I", "B"] \ No newline at end of file +select = ["E", "F", "I", "B"] From bfb59fc6d09e6e0032642777b1860f7044af6462 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 05:05:46 +0900 Subject: [PATCH 08/19] docs(traceability): record analysis deprecation policy --- .../analysis-deprecation-warning-policy.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docs/traceability/analysis-deprecation-warning-policy.md diff --git a/docs/traceability/analysis-deprecation-warning-policy.md b/docs/traceability/analysis-deprecation-warning-policy.md new file mode 100644 index 000000000..062ff5c34 --- /dev/null +++ b/docs/traceability/analysis-deprecation-warning-policy.md @@ -0,0 +1,77 @@ +# Analysis deprecation-warning policy + +Status: Proposed + +## Problem + +Protected `develop@314ddeae7b775a4957594b599358c8255617eb2e` configured analysis-engine pytest with a repository-wide `ignore::DeprecationWarning`. The same protected tree also suppressed every `DeprecationWarning` attributed to `^audioread` around three production decode paths. Those rules made it impossible to distinguish resolved compatibility churn from a newly introduced deprecated API. + +The analysis lock resolves `audioread==3.1.0`. Upstream's 3.1.0 history records Python 3.12/3.13 support and replacement of the deprecated `aifc` and `sunau` standard-library modules. librosa 0.11.0 separately documents audioread support itself as deprecated and planned for removal in librosa 1.0. A module-wide ignore therefore has no defensible removal condition: it can outlive the warning that originally motivated it and hide a different warning later. + +References: + +- audioread 3.1.0 README/version history: https://github.com/beetbox/audioread/blob/v3.1.0/README.rst +- librosa 0.11.0 advanced I/O documentation: https://librosa.org/doc/0.11.0/ioformats.html + +## Constraints + +- Do not change audio decode parameters, supported formats, dependencies, lockfiles, or MIR behavior merely to make warning output quiet. +- Do not turn a warning failure into success by restoring a global ignore, broad module ignore, test exclusion, `noqa`, or gate change. +- Third-party warnings may be suppressed only when the exact category/message/module and upstream cause are known and there is a concrete removal condition. +- A Draft branch is not protected product truth. Fresh exact-head tests and cross-platform CI must expose any warning hidden by the previous policy. + +## Alternatives considered + +### Keep the global pytest ignore + +Rejected. It makes all `DeprecationWarning` instances invisible, including BandScope-owned deprecated calls and new dependency regressions. + +### Revert to Python's default warning behavior + +Rejected for CI. Default filtering can hide repeated or location-dependent deprecations and does not provide a fail-closed acceptance gate. + +### Keep broad `^audioread` deprecation filters only around decode + +Rejected. The filter is keyed only by module and category, so it cannot distinguish the historical Python-compatibility warning from a future unrelated audioread deprecation. The locked audioread version has already replaced the deprecated stdlib modules that motivated the old compatibility class. + +### Fail tests on deprecations and remove the broad runtime filters + +Selected. Unowned deprecations become test failures. Known third-party exceptions, if still needed after execution, must be narrower than the removed rules and carry upstream/removal evidence. + +## Implementation evidence + +- `c9a996912e73c2a5aa0046f8afec5518c892b2e6`: source-level RED requiring pytest not to hide all deprecations. +- `5093dce9425d94b5dc38b54273e635a42d0648aa`: switch pytest to `error::DeprecationWarning`. +- `7885f89c59d3e4b2296efc2fee0379bb09af33f7`: source-level RED rejecting blanket audioread deprecation filters in Temporal Analysis, Transcription, and Separation. +- `ff57338040adfd42404c8066f2ac932aa3e94e82`, `594ea6de4ebb776e8cd7d41299339299a90d7f1a`, `434baa1bd3e27b8a49d8751334a27ffacb51f830`: remove those production suppressions without changing decode arguments or dependencies. +- `0364d68200a9822f3df2d722164ab3f1a6d89c07`: preserve the existing `pyproject.toml` final newline after the policy edit. + +These commits prove the policy/source change only. They do not prove that the complete analysis suite is warning-clean; that requires terminal exact-head execution after this document is committed. + +## Risks and effects + +A previously hidden dependency deprecation may now fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. + +The change does not itself remove librosa's deprecated audioread fallback. Format-support changes require separate buyer-facing evidence because forcing a new decoder path can alter which real audio files BandScope accepts. + +## Follow-up + +1. Run the full analysis suite with the new fail-on-deprecation policy. +2. Record every distinct warning by category, message, originating module/package, and call path. +3. Repair owned deprecated calls and rerun the focused/full suites. +4. Audit the remaining `FutureWarning` and message/module-scoped compatibility filters separately; do not treat their existence on protected `develop` as permanent acceptance. +5. Keep the PR Draft until exact-head repository/central gates and an independent non-author review are complete. + +## Security Notes + +### Trust boundary + +Dependency/runtime warning output is diagnostic input to the analysis acceptance gate. A suppression rule changes which dependency and owned-code regressions become review-visible evidence. + +### Mitigations + +The pytest policy fails on deprecations by default, and a regression test rejects the known module-wide audioread deprecation filters in production audio loaders. Any future exception must be narrower and evidence-backed rather than restoring the removed blanket rules. + +### Remaining risk + +This policy does not make third-party dependency behavior safe by itself and does not yet prove zero warnings on every supported Windows/macOS runtime. Exact-head hosted execution remains required. From fec1d8e9c4f320dd38a803b81f1310eb1238e563 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 06:00:32 +0900 Subject: [PATCH 09/19] test(analysis): reject blanket audioread future warnings --- .../tests/test_deprecation_warning_policy.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/services/analysis-engine/tests/test_deprecation_warning_policy.py b/services/analysis-engine/tests/test_deprecation_warning_policy.py index 9259a1bcd..00b2429bd 100644 --- a/services/analysis-engine/tests/test_deprecation_warning_policy.py +++ b/services/analysis-engine/tests/test_deprecation_warning_policy.py @@ -14,8 +14,8 @@ ) -def _is_blanket_audioread_deprecation_filter(call: ast.Call) -> bool: - """Return whether one call hides every audioread ``DeprecationWarning``.""" +def _is_blanket_audioread_warning_filter(call: ast.Call) -> bool: + """Return whether one call hides a whole audioread warning category.""" if not isinstance(call.func, ast.Attribute) or call.func.attr != "filterwarnings": return False if not call.args or not isinstance(call.args[0], ast.Constant): @@ -26,11 +26,13 @@ def _is_blanket_audioread_deprecation_filter(call: ast.Call) -> bool: keywords = {keyword.arg: keyword.value for keyword in call.keywords if keyword.arg} category = keywords.get("category") module = keywords.get("module") + message = keywords.get("message") return ( isinstance(category, ast.Name) - and category.id == "DeprecationWarning" + and category.id in {"DeprecationWarning", "FutureWarning"} and isinstance(module, ast.Constant) and module.value == "^audioread" + and message is None ) @@ -44,13 +46,13 @@ def test_pytest_fails_on_unowned_deprecation_warnings() -> None: assert "ignore::DeprecationWarning" not in filters -def test_audio_loaders_do_not_blanket_hide_audioread_deprecations() -> None: - """Keep audio loaders from suppressing every audioread deprecation at runtime.""" +def test_audio_loaders_do_not_blanket_hide_audioread_warnings() -> None: + """Keep audio loaders from hiding whole audioread warning categories.""" offenders: list[str] = [] for path in _AUDIO_LOADER_PATHS: tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) if any( - _is_blanket_audioread_deprecation_filter(node) + _is_blanket_audioread_warning_filter(node) for node in ast.walk(tree) if isinstance(node, ast.Call) ): From e5197fb25bbb2c5f9ae412e24a14d69f3071753f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 06:00:49 +0900 Subject: [PATCH 10/19] fix(analysis): expose audioread future warnings --- .../analysis-engine/src/bandscope_analysis/temporal/analyzer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py index 6020c9cf6..9528cfee7 100644 --- a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py +++ b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py @@ -85,8 +85,6 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: ) with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=FutureWarning, module=r"^audioread") - # Keep the loader's known third-party churn quiet without hiding # unrelated decoder warnings that tests and callers should see. for category, message, module in KNOWN_LIBROSA_NUMBA_WARNING_FILTERS: From 072ebf5714489d0f840d02a12e0344d37dfe35f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 06:01:19 +0900 Subject: [PATCH 11/19] fix(separation): expose audioread future warnings --- .../src/bandscope_analysis/separation/audio_separator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py index cb3331a5e..1d2a38d1f 100644 --- a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py +++ b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py @@ -201,7 +201,6 @@ def _load_audio(self, path: Path) -> tuple[AudioStemArray, int]: ) with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=FutureWarning, module=r"^audioread") for category, message, module in KNOWN_LIBROSA_NUMBA_WARNING_FILTERS: warnings.filterwarnings( "ignore", From 44ca7530fa4f2d2f81a89bf71afe69421b79fd0b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 06:01:44 +0900 Subject: [PATCH 12/19] docs(traceability): record audioread future-warning boundary --- .../analysis-deprecation-warning-policy.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/traceability/analysis-deprecation-warning-policy.md b/docs/traceability/analysis-deprecation-warning-policy.md index 062ff5c34..6ef52e8c2 100644 --- a/docs/traceability/analysis-deprecation-warning-policy.md +++ b/docs/traceability/analysis-deprecation-warning-policy.md @@ -6,6 +6,8 @@ Status: Proposed Protected `develop@314ddeae7b775a4957594b599358c8255617eb2e` configured analysis-engine pytest with a repository-wide `ignore::DeprecationWarning`. The same protected tree also suppressed every `DeprecationWarning` attributed to `^audioread` around three production decode paths. Those rules made it impossible to distinguish resolved compatibility churn from a newly introduced deprecated API. +The first repair removed those deprecation suppressions but left module-wide `FutureWarning` ignores for `^audioread` in Temporal Analysis and Separation. Those filters had the same structural defect: category + module with no exact message, upstream cause, or removal condition. They could therefore hide a future audioread compatibility change unrelated to the warning that originally motivated the filter. + The analysis lock resolves `audioread==3.1.0`. Upstream's 3.1.0 history records Python 3.12/3.13 support and replacement of the deprecated `aifc` and `sunau` standard-library modules. librosa 0.11.0 separately documents audioread support itself as deprecated and planned for removal in librosa 1.0. A module-wide ignore therefore has no defensible removal condition: it can outlive the warning that originally motivated it and hide a different warning later. References: @@ -30,9 +32,9 @@ Rejected. It makes all `DeprecationWarning` instances invisible, including BandS Rejected for CI. Default filtering can hide repeated or location-dependent deprecations and does not provide a fail-closed acceptance gate. -### Keep broad `^audioread` deprecation filters only around decode +### Keep broad `^audioread` warning filters around decode -Rejected. The filter is keyed only by module and category, so it cannot distinguish the historical Python-compatibility warning from a future unrelated audioread deprecation. The locked audioread version has already replaced the deprecated stdlib modules that motivated the old compatibility class. +Rejected. A category + module filter cannot distinguish one historical compatibility warning from a future unrelated warning in the same package. This applies to both `DeprecationWarning` and `FutureWarning`. If a temporary third-party exception is required, it must also bind the exact message and carry upstream/removal evidence. ### Fail tests on deprecations and remove the broad runtime filters @@ -43,14 +45,16 @@ Selected. Unowned deprecations become test failures. Known third-party exception - `c9a996912e73c2a5aa0046f8afec5518c892b2e6`: source-level RED requiring pytest not to hide all deprecations. - `5093dce9425d94b5dc38b54273e635a42d0648aa`: switch pytest to `error::DeprecationWarning`. - `7885f89c59d3e4b2296efc2fee0379bb09af33f7`: source-level RED rejecting blanket audioread deprecation filters in Temporal Analysis, Transcription, and Separation. -- `ff57338040adfd42404c8066f2ac932aa3e94e82`, `594ea6de4ebb776e8cd7d41299339299a90d7f1a`, `434baa1bd3e27b8a49d8751334a27ffacb51f830`: remove those production suppressions without changing decode arguments or dependencies. +- `ff57338040adfd42404c8066f2ac932aa3e94e82`, `594ea6de4ebb776e8cd7d41299339299a90d7f1a`, `434baa1bd3e27b8a49d8751334a27ffacb51f830`: remove those production deprecation suppressions without changing decode arguments or dependencies. - `0364d68200a9822f3df2d722164ab3f1a6d89c07`: preserve the existing `pyproject.toml` final newline after the policy edit. +- `fec1d8e9c4f320dd38a803b81f1310eb1238e563`: extend the source-level policy RED so category + `^audioread` filters without an exact message are rejected for both `DeprecationWarning` and `FutureWarning`. +- `e5197fb25bbb2c5f9ae412e24a14d69f3071753f` and `072ebf5714489d0f840d02a12e0344d37dfe35f4`: remove the remaining blanket audioread `FutureWarning` suppressions from Temporal Analysis and Separation. Transcription had no remaining `FutureWarning` suppression. These commits prove the policy/source change only. They do not prove that the complete analysis suite is warning-clean; that requires terminal exact-head execution after this document is committed. ## Risks and effects -A previously hidden dependency deprecation may now fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. +A previously hidden dependency warning may now become visible, and an unowned deprecation may fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. The change does not itself remove librosa's deprecated audioread fallback. Format-support changes require separate buyer-facing evidence because forcing a new decoder path can alter which real audio files BandScope accepts. @@ -59,7 +63,7 @@ The change does not itself remove librosa's deprecated audioread fallback. Forma 1. Run the full analysis suite with the new fail-on-deprecation policy. 2. Record every distinct warning by category, message, originating module/package, and call path. 3. Repair owned deprecated calls and rerun the focused/full suites. -4. Audit the remaining `FutureWarning` and message/module-scoped compatibility filters separately; do not treat their existence on protected `develop` as permanent acceptance. +4. Audit the remaining message/module-scoped librosa/Numba compatibility filters against the observed warning inventory and retain one only with an upstream cause and concrete removal condition. 5. Keep the PR Draft until exact-head repository/central gates and an independent non-author review are complete. ## Security Notes @@ -70,7 +74,7 @@ Dependency/runtime warning output is diagnostic input to the analysis acceptance ### Mitigations -The pytest policy fails on deprecations by default, and a regression test rejects the known module-wide audioread deprecation filters in production audio loaders. Any future exception must be narrower and evidence-backed rather than restoring the removed blanket rules. +The pytest policy fails on deprecations by default, and the source-policy regression rejects module-wide audioread ignores for both deprecation and future-warning categories when no exact message is supplied. Any future exception must be narrower and evidence-backed rather than restoring a removed blanket rule. ### Remaining risk From 10b5d28ced09c64f148e3b98b7d924357bc20cd4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:02:41 +0900 Subject: [PATCH 13/19] test(analysis): fail on unowned future warnings --- .../analysis-engine/tests/test_deprecation_warning_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_deprecation_warning_policy.py b/services/analysis-engine/tests/test_deprecation_warning_policy.py index 00b2429bd..923c5c571 100644 --- a/services/analysis-engine/tests/test_deprecation_warning_policy.py +++ b/services/analysis-engine/tests/test_deprecation_warning_policy.py @@ -37,13 +37,15 @@ def _is_blanket_audioread_warning_filter(call: ast.Call) -> bool: def test_pytest_fails_on_unowned_deprecation_warnings() -> None: - """Require pytest to turn an unowned deprecation into a test failure.""" + """Require pytest to turn unowned deprecation/future warnings into failures.""" pyproject_path = _ANALYSIS_ROOT / "pyproject.toml" config = tomllib.loads(pyproject_path.read_text(encoding="utf-8")) filters = config["tool"]["pytest"]["ini_options"].get("filterwarnings", []) assert "error::DeprecationWarning" in filters assert "ignore::DeprecationWarning" not in filters + assert "error::FutureWarning" in filters + assert "ignore::FutureWarning" not in filters def test_audio_loaders_do_not_blanket_hide_audioread_warnings() -> None: From 21bbeedcf913d2bbf46ba6ac9a4d5797469ce565 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:02:49 +0900 Subject: [PATCH 14/19] fix(analysis): surface unowned future warnings --- services/analysis-engine/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/analysis-engine/pyproject.toml b/services/analysis-engine/pyproject.toml index ca28f2e9d..f4e63f100 100644 --- a/services/analysis-engine/pyproject.toml +++ b/services/analysis-engine/pyproject.toml @@ -34,6 +34,7 @@ testpaths = ["tests"] pythonpath = ["src"] filterwarnings = [ "error::DeprecationWarning", + "error::FutureWarning", ] [tool.coverage.run] From 49845a7a897167be9f789c9ab13ba3eb0e7993ca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:03:23 +0900 Subject: [PATCH 15/19] docs(traceability): bind future warnings to CI failure --- .../analysis-deprecation-warning-policy.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/traceability/analysis-deprecation-warning-policy.md b/docs/traceability/analysis-deprecation-warning-policy.md index 6ef52e8c2..f2e396bb3 100644 --- a/docs/traceability/analysis-deprecation-warning-policy.md +++ b/docs/traceability/analysis-deprecation-warning-policy.md @@ -8,10 +8,13 @@ Protected `develop@314ddeae7b775a4957594b599358c8255617eb2e` configured analysis The first repair removed those deprecation suppressions but left module-wide `FutureWarning` ignores for `^audioread` in Temporal Analysis and Separation. Those filters had the same structural defect: category + module with no exact message, upstream cause, or removal condition. They could therefore hide a future audioread compatibility change unrelated to the warning that originally motivated the filter. +A second policy gap remained after those source filters were removed: pytest failed closed on `DeprecationWarning`, but not on `FutureWarning`. Python distinguishes the two by intended audience, not by whether the warning can precede a breaking API change. Leaving `FutureWarning` at default handling would therefore let an end-user-facing compatibility warning appear in CI without making the warning audit gate fail. + The analysis lock resolves `audioread==3.1.0`. Upstream's 3.1.0 history records Python 3.12/3.13 support and replacement of the deprecated `aifc` and `sunau` standard-library modules. librosa 0.11.0 separately documents audioread support itself as deprecated and planned for removal in librosa 1.0. A module-wide ignore therefore has no defensible removal condition: it can outlive the warning that originally motivated it and hide a different warning later. References: +- Python warnings control: https://docs.python.org/3.13/library/warnings.html - audioread 3.1.0 README/version history: https://github.com/beetbox/audioread/blob/v3.1.0/README.rst - librosa 0.11.0 advanced I/O documentation: https://librosa.org/doc/0.11.0/ioformats.html @@ -30,15 +33,15 @@ Rejected. It makes all `DeprecationWarning` instances invisible, including BandS ### Revert to Python's default warning behavior -Rejected for CI. Default filtering can hide repeated or location-dependent deprecations and does not provide a fail-closed acceptance gate. +Rejected for CI. Python's warnings filter can ignore or only display categories depending on defaults and location. The analysis acceptance gate needs an observed `DeprecationWarning` or `FutureWarning` to fail rather than merely appear in logs. ### Keep broad `^audioread` warning filters around decode Rejected. A category + module filter cannot distinguish one historical compatibility warning from a future unrelated warning in the same package. This applies to both `DeprecationWarning` and `FutureWarning`. If a temporary third-party exception is required, it must also bind the exact message and carry upstream/removal evidence. -### Fail tests on deprecations and remove the broad runtime filters +### Fail tests on deprecation/future warnings and remove the broad runtime filters -Selected. Unowned deprecations become test failures. Known third-party exceptions, if still needed after execution, must be narrower than the removed rules and carry upstream/removal evidence. +Selected. Unowned `DeprecationWarning` and `FutureWarning` instances become test failures. Known third-party exceptions, if still needed after execution, must be narrower than the removed rules and carry upstream/removal evidence. ## Implementation evidence @@ -49,18 +52,20 @@ Selected. Unowned deprecations become test failures. Known third-party exception - `0364d68200a9822f3df2d722164ab3f1a6d89c07`: preserve the existing `pyproject.toml` final newline after the policy edit. - `fec1d8e9c4f320dd38a803b81f1310eb1238e563`: extend the source-level policy RED so category + `^audioread` filters without an exact message are rejected for both `DeprecationWarning` and `FutureWarning`. - `e5197fb25bbb2c5f9ae412e24a14d69f3071753f` and `072ebf5714489d0f840d02a12e0344d37dfe35f4`: remove the remaining blanket audioread `FutureWarning` suppressions from Temporal Analysis and Separation. Transcription had no remaining `FutureWarning` suppression. +- `10b5d28ced09c64f148e3b98b7d924357bc20cd4`: policy RED requiring pytest to fail on unowned `FutureWarning` and forbidding a global future-warning ignore. +- `21bbeedcf913d2bbf46ba6ac9a4d5797469ce565`: add `error::FutureWarning` beside the existing deprecation error policy without changing dependencies or decode behavior. These commits prove the policy/source change only. They do not prove that the complete analysis suite is warning-clean; that requires terminal exact-head execution after this document is committed. ## Risks and effects -A previously hidden dependency warning may now become visible, and an unowned deprecation may fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. +A previously non-fatal dependency `FutureWarning` or hidden `DeprecationWarning` may now fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. The change does not itself remove librosa's deprecated audioread fallback. Format-support changes require separate buyer-facing evidence because forcing a new decoder path can alter which real audio files BandScope accepts. ## Follow-up -1. Run the full analysis suite with the new fail-on-deprecation policy. +1. Run the full analysis suite with the fail-on-deprecation/future-warning policy. 2. Record every distinct warning by category, message, originating module/package, and call path. 3. Repair owned deprecated calls and rerun the focused/full suites. 4. Audit the remaining message/module-scoped librosa/Numba compatibility filters against the observed warning inventory and retain one only with an upstream cause and concrete removal condition. @@ -74,7 +79,7 @@ Dependency/runtime warning output is diagnostic input to the analysis acceptance ### Mitigations -The pytest policy fails on deprecations by default, and the source-policy regression rejects module-wide audioread ignores for both deprecation and future-warning categories when no exact message is supplied. Any future exception must be narrower and evidence-backed rather than restoring a removed blanket rule. +The pytest policy fails on both `DeprecationWarning` and `FutureWarning`, and the source-policy regression rejects module-wide audioread ignores for those categories when no exact message is supplied. Any future exception must be narrower and evidence-backed rather than restoring a removed blanket rule. ### Remaining risk From fa7adf40294f25325b60cb5abb7cd8f073cb070b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:04:59 +0900 Subject: [PATCH 16/19] test(analysis): reject runtime warning suppression --- .../tests/test_deprecation_warning_policy.py | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/services/analysis-engine/tests/test_deprecation_warning_policy.py b/services/analysis-engine/tests/test_deprecation_warning_policy.py index 923c5c571..702c2b5b5 100644 --- a/services/analysis-engine/tests/test_deprecation_warning_policy.py +++ b/services/analysis-engine/tests/test_deprecation_warning_policy.py @@ -1,4 +1,4 @@ -"""Tests for analysis-engine deprecation-warning visibility policy.""" +"""Tests for analysis-engine warning visibility policy.""" from __future__ import annotations @@ -14,29 +14,16 @@ ) -def _is_blanket_audioread_warning_filter(call: ast.Call) -> bool: - """Return whether one call hides a whole audioread warning category.""" +def _is_runtime_warning_ignore(call: ast.Call) -> bool: + """Return whether one production call hides warnings at runtime.""" if not isinstance(call.func, ast.Attribute) or call.func.attr != "filterwarnings": return False if not call.args or not isinstance(call.args[0], ast.Constant): return False - if call.args[0].value != "ignore": - return False - - keywords = {keyword.arg: keyword.value for keyword in call.keywords if keyword.arg} - category = keywords.get("category") - module = keywords.get("module") - message = keywords.get("message") - return ( - isinstance(category, ast.Name) - and category.id in {"DeprecationWarning", "FutureWarning"} - and isinstance(module, ast.Constant) - and module.value == "^audioread" - and message is None - ) + return call.args[0].value == "ignore" -def test_pytest_fails_on_unowned_deprecation_warnings() -> None: +def test_pytest_fails_on_unowned_compatibility_warnings() -> None: """Require pytest to turn unowned deprecation/future warnings into failures.""" pyproject_path = _ANALYSIS_ROOT / "pyproject.toml" config = tomllib.loads(pyproject_path.read_text(encoding="utf-8")) @@ -48,13 +35,13 @@ def test_pytest_fails_on_unowned_deprecation_warnings() -> None: assert "ignore::FutureWarning" not in filters -def test_audio_loaders_do_not_blanket_hide_audioread_warnings() -> None: - """Keep audio loaders from hiding whole audioread warning categories.""" +def test_audio_loaders_do_not_hide_runtime_warnings() -> None: + """Keep production audio loaders from converting warning findings into silence.""" offenders: list[str] = [] for path in _AUDIO_LOADER_PATHS: tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) if any( - _is_blanket_audioread_warning_filter(node) + _is_runtime_warning_ignore(node) for node in ast.walk(tree) if isinstance(node, ast.Call) ): From 505d3e14b4559949b6c9cef97bb5b472ea298ae0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:05:16 +0900 Subject: [PATCH 17/19] fix(analysis): stop suppressing loader warnings --- .../bandscope_analysis/temporal/analyzer.py | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py index 9528cfee7..9746b48b4 100644 --- a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py +++ b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py @@ -4,7 +4,6 @@ import logging import os -import warnings from pathlib import Path from typing import Any @@ -20,10 +19,6 @@ TARGET_SR = 44100 MAX_AUDIO_FILE_BYTES = 100 * 1024 * 1024 # 100 MiB MAX_ANALYSIS_DURATION_SECONDS = 15 * 60 # 15 minutes -KNOWN_LIBROSA_NUMBA_WARNING_FILTERS = ( - (DeprecationWarning, r".*pkg_resources is deprecated.*", r".*librosa.*"), - (FutureWarning, r".*Numba.*", r".*numba.*"), -) # ponytail: assumes 4/4; upgrade to meter estimation or a madmom DBN if other meters matter. BEATS_PER_BAR = 4 @@ -84,23 +79,14 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: f"(max {MAX_AUDIO_FILE_BYTES} bytes)" ) - with warnings.catch_warnings(): - # Keep the loader's known third-party churn quiet without hiding - # unrelated decoder warnings that tests and callers should see. - for category, message, module in KNOWN_LIBROSA_NUMBA_WARNING_FILTERS: - warnings.filterwarnings( - "ignore", - category=category, - message=message, - module=module, - ) - # Load audio, converting to mono and standardizing sample rate - y, sr = librosa.load( - fileobj, - sr=TARGET_SR, - mono=True, - duration=MAX_ANALYSIS_DURATION_SECONDS, - ) + # Load audio, converting to mono and standardizing sample rate. + # Warnings remain visible so test/CI policy can root-cause them. + y, sr = librosa.load( + fileobj, + sr=TARGET_SR, + mono=True, + duration=MAX_ANALYSIS_DURATION_SECONDS, + ) # Ensure it's a 1D float array for librosa if not isinstance(y, np.ndarray): From 3d94c2f5c5d57d91ded1bba211429e2af71b1537 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:05:39 +0900 Subject: [PATCH 18/19] fix(analysis): expose separation loader warnings --- .../separation/audio_separator.py | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py index 1d2a38d1f..603f12fb6 100644 --- a/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py +++ b/services/analysis-engine/src/bandscope_analysis/separation/audio_separator.py @@ -23,7 +23,6 @@ import logging import os import sys -import warnings from dataclasses import dataclass from pathlib import Path from typing import Any, cast @@ -32,7 +31,6 @@ import numpy as np from bandscope_analysis.temporal.analyzer import ( - KNOWN_LIBROSA_NUMBA_WARNING_FILTERS, MAX_ANALYSIS_DURATION_SECONDS, MAX_AUDIO_FILE_BYTES, TARGET_SR, @@ -200,20 +198,12 @@ def _load_audio(self, path: Path) -> tuple[AudioStemArray, int]: f"{file_size} bytes (max {self.config.max_file_bytes} bytes)" ) - with warnings.catch_warnings(): - for category, message, module in KNOWN_LIBROSA_NUMBA_WARNING_FILTERS: - warnings.filterwarnings( - "ignore", - category=category, - message=message, - module=module, - ) - y, sr = librosa.load( - fileobj, - sr=self.config.target_sample_rate, - mono=True, - duration=self.config.max_duration_seconds, - ) + y, sr = librosa.load( + fileobj, + sr=self.config.target_sample_rate, + mono=True, + duration=self.config.max_duration_seconds, + ) except ValueError: raise except Exception as error: From 39ecb0eef845b2cadea2fd349ce29a37c59b4a73 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 18 Sep 2026 07:06:05 +0900 Subject: [PATCH 19/19] docs(traceability): remove pre-evidence warning exceptions --- .../analysis-deprecation-warning-policy.md | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/traceability/analysis-deprecation-warning-policy.md b/docs/traceability/analysis-deprecation-warning-policy.md index f2e396bb3..68588ec51 100644 --- a/docs/traceability/analysis-deprecation-warning-policy.md +++ b/docs/traceability/analysis-deprecation-warning-policy.md @@ -10,7 +10,9 @@ The first repair removed those deprecation suppressions but left module-wide `Fu A second policy gap remained after those source filters were removed: pytest failed closed on `DeprecationWarning`, but not on `FutureWarning`. Python distinguishes the two by intended audience, not by whether the warning can precede a breaking API change. Leaving `FutureWarning` at default handling would therefore let an end-user-facing compatibility warning appear in CI without making the warning audit gate fail. -The analysis lock resolves `audioread==3.1.0`. Upstream's 3.1.0 history records Python 3.12/3.13 support and replacement of the deprecated `aifc` and `sunau` standard-library modules. librosa 0.11.0 separately documents audioread support itself as deprecated and planned for removal in librosa 1.0. A module-wide ignore therefore has no defensible removal condition: it can outlive the warning that originally motivated it and hide a different warning later. +The three audio loaders also retained message/module-scoped librosa/Numba ignores. Those exceptions still converted dependency warnings into silence before pytest could observe them, while the branch had no exact-head warning inventory proving that either exception was currently necessary. Keeping a suppression first and asking CI to inventory warnings later is circular: the gate cannot report a warning that production code has already discarded. + +The analysis lock resolves `audioread==3.1.0`. Upstream's 3.1.0 history records Python 3.12/3.13 support and replacement of the deprecated `aifc` and `sunau` standard-library modules. librosa 0.11.0 separately documents audioread support itself as deprecated and planned for removal in librosa 1.0. A warning ignore without current execution evidence can outlive the warning that originally motivated it and hide a different warning later. References: @@ -22,8 +24,8 @@ References: - Do not change audio decode parameters, supported formats, dependencies, lockfiles, or MIR behavior merely to make warning output quiet. - Do not turn a warning failure into success by restoring a global ignore, broad module ignore, test exclusion, `noqa`, or gate change. -- Third-party warnings may be suppressed only when the exact category/message/module and upstream cause are known and there is a concrete removal condition. -- A Draft branch is not protected product truth. Fresh exact-head tests and cross-platform CI must expose any warning hidden by the previous policy. +- A temporary third-party warning suppression requires an observed exact-head warning plus exact category/message/module, upstream cause, and a concrete removal condition. It must not be pre-installed before that evidence exists. +- A Draft branch is not protected product truth. Fresh exact-head tests and cross-platform CI must expose warnings hidden by the previous policy. ## Alternatives considered @@ -37,11 +39,15 @@ Rejected for CI. Python's warnings filter can ignore or only display categories ### Keep broad `^audioread` warning filters around decode -Rejected. A category + module filter cannot distinguish one historical compatibility warning from a future unrelated warning in the same package. This applies to both `DeprecationWarning` and `FutureWarning`. If a temporary third-party exception is required, it must also bind the exact message and carry upstream/removal evidence. +Rejected. A category + module filter cannot distinguish one historical compatibility warning from a future unrelated warning in the same package. This applies to both `DeprecationWarning` and `FutureWarning`. + +### Keep the existing librosa/Numba warning exceptions until CI proves they are stale + +Rejected. Runtime suppression prevents CI from observing the very warning needed to justify, repair, or remove the exception. Without current exact-head evidence, a pre-existing ignore has no testable necessity or removal trigger. -### Fail tests on deprecation/future warnings and remove the broad runtime filters +### Fail tests on compatibility warnings and expose loader warnings to the gate -Selected. Unowned `DeprecationWarning` and `FutureWarning` instances become test failures. Known third-party exceptions, if still needed after execution, must be narrower than the removed rules and carry upstream/removal evidence. +Selected. Unowned `DeprecationWarning` and `FutureWarning` instances become test failures, and the three production audio loaders no longer discard warnings before pytest can classify them. If exact-head execution later proves that an upstream-only warning cannot yet be repaired, a narrow temporary exception can be reintroduced only with the observed warning identity, upstream evidence, regression coverage, and removal condition. ## Implementation evidence @@ -54,6 +60,9 @@ Selected. Unowned `DeprecationWarning` and `FutureWarning` instances become test - `e5197fb25bbb2c5f9ae412e24a14d69f3071753f` and `072ebf5714489d0f840d02a12e0344d37dfe35f4`: remove the remaining blanket audioread `FutureWarning` suppressions from Temporal Analysis and Separation. Transcription had no remaining `FutureWarning` suppression. - `10b5d28ced09c64f148e3b98b7d924357bc20cd4`: policy RED requiring pytest to fail on unowned `FutureWarning` and forbidding a global future-warning ignore. - `21bbeedcf913d2bbf46ba6ac9a4d5797469ce565`: add `error::FutureWarning` beside the existing deprecation error policy without changing dependencies or decode behavior. +- `fa7adf40294f25325b60cb5abb7cd8f073cb070b`: source-policy RED requiring the three production audio loaders not to install runtime `ignore` filters before warning evidence exists. +- `505d3e14b4559949b6c9cef97bb5b472ea298ae0`: remove Temporal Analysis' remaining librosa/Numba runtime suppression and its warning plumbing. +- `3d94c2f5c5d57d91ded1bba211429e2af71b1537`: remove Separation's remaining librosa/Numba runtime suppression and inherited warning-filter dependency on Temporal Analysis. These commits prove the policy/source change only. They do not prove that the complete analysis suite is warning-clean; that requires terminal exact-head execution after this document is committed. @@ -61,14 +70,14 @@ These commits prove the policy/source change only. They do not prove that the co A previously non-fatal dependency `FutureWarning` or hidden `DeprecationWarning` may now fail CI. That is an intended diagnostic outcome, not a compatibility claim. The causal response is to inspect the originating package/module/call path, migrate BandScope-owned use, upgrade or change a dependency call path where compatible, or document an exact temporary third-party exception with a removal condition. -The change does not itself remove librosa's deprecated audioread fallback. Format-support changes require separate buyer-facing evidence because forcing a new decoder path can alter which real audio files BandScope accepts. +Removing the loader suppressions does not change sample rate, mono conversion, duration limits, supported formats, or the librosa call path. It only restores warning observability. The change does not itself remove librosa's deprecated audioread fallback; format-support changes require separate buyer-facing evidence because forcing a new decoder path can alter which real audio files BandScope accepts. ## Follow-up -1. Run the full analysis suite with the fail-on-deprecation/future-warning policy. +1. Run the full analysis suite with the fail-on-deprecation/future-warning policy and no loader-side ignores. 2. Record every distinct warning by category, message, originating module/package, and call path. 3. Repair owned deprecated calls and rerun the focused/full suites. -4. Audit the remaining message/module-scoped librosa/Numba compatibility filters against the observed warning inventory and retain one only with an upstream cause and concrete removal condition. +4. For an upstream-only warning that cannot yet be repaired, add no suppression until its exact identity, upstream cause, regression coverage, and removal condition are documented. 5. Keep the PR Draft until exact-head repository/central gates and an independent non-author review are complete. ## Security Notes @@ -79,7 +88,7 @@ Dependency/runtime warning output is diagnostic input to the analysis acceptance ### Mitigations -The pytest policy fails on both `DeprecationWarning` and `FutureWarning`, and the source-policy regression rejects module-wide audioread ignores for those categories when no exact message is supplied. Any future exception must be narrower and evidence-backed rather than restoring a removed blanket rule. +The pytest policy fails on both `DeprecationWarning` and `FutureWarning`, and the source-policy regression prevents the three production audio loaders from discarding runtime warnings before the gate observes them. Any future exception requires evidence rather than inheriting a legacy ignore. ### Remaining risk