From 7662eb71169717ea558d93a1dfdefd58c29af4e7 Mon Sep 17 00:00:00 2001 From: JuliaEdom Date: Mon, 29 Jun 2026 18:14:04 +0300 Subject: [PATCH] docs(mutmut): document why the mutation gate covers only retry.py The [tool.mutmut] policy declares 7 security-critical serving modules as mutation targets (enforced by test_mutmut_policy.py), but the CI gate (scripts/mutation_report.py) mutates only the duckdb-free retry.py. Those serving modules transitively import duckdb, whose compiled subpackage (_duckdb._sqltypes) fails to import inside mutmut's mutants/ workspace and crashes the run with every mutant left "not checked" -- reproduced in a Linux container, so it is a mutmut x duckdb harness limitation, not platform-specific. Make this gap explicit at the three places a reader meets it (the pyproject policy list, the runner's MODULE_TARGETS, and the policy test) so declared coverage is not mistaken for live mutation coverage. Comments only; no logic change. Extending the gate to serving modules needs isolated mutant execution (subprocess/spawn) or a different mutation tool. Co-Authored-By: Claude Opus 4.8 (1M context) --- pyproject.toml | 12 ++++++++++++ scripts/mutation_report.py | 8 ++++++++ tests/unit/test_mutmut_policy.py | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index a06380c..17d48d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,6 +201,18 @@ markers = [ asyncio_mode = "auto" [tool.mutmut] +# This list is the *declared* mutation-target policy, enforced by +# tests/unit/test_mutmut_policy.py (paths exist, define real logic, cover the +# security-critical surfaces). It is NOT what the CI mutation gate actually +# mutates: scripts/mutation_report.py drives the gate from its own +# MODULE_TARGETS and currently mutates only the duckdb-free +# sdk/agentflow/retry.py. The serving modules below cannot be mutation-tested +# with the current harness -- they transitively import duckdb, whose compiled +# subpackage (_duckdb._sqltypes) fails to import inside mutmut's mutants/ +# workspace and crashes the run (reproduced on Linux, so it is a mutmut x +# duckdb limitation, not platform-specific). Extending the gate needs isolated +# mutant execution (subprocess/spawn) or a different mutation tool. See +# scripts/mutation_report.py. paths_to_mutate = [ "src/serving/api/auth/manager.py", "src/serving/api/auth/key_rotation.py", diff --git a/scripts/mutation_report.py b/scripts/mutation_report.py index 979ca13..c36528b 100644 --- a/scripts/mutation_report.py +++ b/scripts/mutation_report.py @@ -23,6 +23,14 @@ class ModuleTarget: tests: tuple[str, ...] +# The CI mutation gate runs exactly these module/test pairs. It is deliberately +# limited to the duckdb-free SDK surface: every serving module in the +# [tool.mutmut] policy (pyproject.toml) transitively imports duckdb, and +# duckdb's compiled subpackage (_duckdb._sqltypes) fails to import inside +# mutmut's mutants/ workspace ("'_duckdb' is not a package"), crashing the run +# with every mutant left "not checked". This is a mutmut x duckdb harness +# limitation (reproduced on Linux), not a missing test. Adding serving targets +# needs isolated mutant execution (subprocess/spawn) or a different tool. MODULE_TARGETS = { Path("agentflow/retry.py"): ModuleTarget( threshold=0.75, diff --git a/tests/unit/test_mutmut_policy.py b/tests/unit/test_mutmut_policy.py index c638860..5edd6c3 100644 --- a/tests/unit/test_mutmut_policy.py +++ b/tests/unit/test_mutmut_policy.py @@ -16,6 +16,11 @@ # pagination SQL wrappers built around prevalidated NL SQL. # - sql_builder: every entity/metric SQL string the engine executes is # assembled here. +# NOTE: these are the *declared* targets (intent). Actual mutation execution is +# gated by scripts/mutation_report.py (MODULE_TARGETS), which currently runs only +# the duckdb-free retry.py -- the serving modules below break mutmut's mutants/ +# workspace via duckdb (see that script's note). These assertions guard the +# declared policy, not live mutation coverage. REQUIRED_MUTATION_TARGETS = { "src/serving/semantic_layer/sql_guard.py", "src/serving/api/auth/manager.py",