mollify 0.1.4
What's new in 0.1.4
Fix release from a full-repository code review (docs/code-review-2026-07-01.md):
every crate was read end-to-end, and every fix below was verified against a
reproduction before landing. Contains breaking changes — fingerprints and
several CLI exit codes changed; regenerate any saved baselines after upgrading
(see Breaking below).
Breaking
- Fingerprints changed wholesale — regenerate baselines (
--save-baseline).
Fingerprints now hash the root-relative path (they no longer vary with
--pathspelling or checkout location, so CI baselines finally transfer to
laptops), drop line numbers in favor of symbol/content identity plus an
occurrence index (edits above a finding no longer churn it), and use the
full 64-bit hash (16 hex chars). - CLI exit codes are stricter where CI trust demanded it: a failed
--save-baselinewrite exits 1 (was: success message + 0); a missing or
invalid--baselinewith--fail-on-regressionexits 1 (was: gate silently
disabled); a nonexistent--pathexits 2 (was: clean 100/100 report);
trace/inspect/list/metricsreject formats they don't implement with
exit 2 (was: silent human output);inspectwith no matching file exits 1. unresolved-importis nowlikely, notcertain: a relative import may
resolve to a C extension or build-generated module the.pywalk can't see.
Fixed
fix --applycorrupted Jupyter notebooks: finding lines are relative to
the concatenated code cells, so notebook findings are no longer
auto-fixable (and the fix planner refuses non-.pyfiles outright).fix --applycould delete live, dynamically-dispatched code: a symbol
invoked from another module viagetattr(lib, "_handler_" + name)()was
gradedcertain; a dynamic sink anywhere in the project now caps
unused-export confidence, mirroringunused-file.fix --applypreserves CRLF line endings and no longer aborts the whole run
(losing the applied count) when one file fails I/O.- Dead-code false positives from resolver gaps: module constants used only in
function signatures (parameter defaults/annotations, return annotations,
lambda defaults); imports inside module-levelwith/for/while/match
suites; symbols used only via lazy in-function imports; sibling modules of a
root-level__init__.py; names shadowed by Python-3-scoped comprehension
targets;__all__ += […]/.extend(…)extensions. - Runtime imports in the
elsebranch of anif TYPE_CHECKING:guard are no
longer treated as type-only (andif not TYPE_CHECKING:now works);
TYPE_CHECKING guard detection is exact instead of substring-based. - Decorated
def/classline numbers point at the definition, not the first
decorator. cold-codecan now actually fire for imported modules: thedefline —
executed at import time — no longer counts as evidence the body ran.- The duplication engine reads notebook code cells, not raw
.ipynbJSON
(near-identical scaffolding produced bogus clone families). - Dependency hygiene: dev-group tools (black, mypy, pre-commit…) are exempt
fromunused-dependency(deptry parity);psycopg2/psycopg2-binaryand
friends are alternative providers instead of a forced alias (no more paired
unused+missing false positives); namespace tops (google,azure, …) are
never guessed asmissing-dependencywithout an installed env; URL/VCS
requirement lines no longer produce mangled names (#egg=respected, pip
comment rules honored). - PEP 440: epochs compare correctly (
2!1.0no longer parses as2), and
.postN/.devNorder per spec instead of comparing equal to the release.
specs_intersectfinds narrow gaps like (>2.0,<2.0.1). - Determinism: import→dist mapping and requirements/pins collection no longer
depend on filesystemread_dirorder; hotspot/coverage/git fallback
matching is anchored at path-separator boundaries (app.pyno longer claims
myapp.py's churn, coverage, or diff hunks) with deterministic tie-breaks;
non-ASCII filenames survive git'score.quotePath. inspect <file>matches path fragments at separator boundaries only
(inspectingb.pyno longer returnslib.py's findings).- Unconstrained declared deps (bare
flask) are matched against advisories
again instead of being silently skipped. - MCP protocol: version negotiation no longer echoes arbitrary client
versions; malformed JSON gets a-32700response; requests without a
methodget-32600;mollify_fixapply errors surface as tool errors. - LSP: unknown requests get
-32601instead of hanging the client;
didCloseclears stale diagnostics; diagnostic ranges are never reversed
and cover the final line; a malformedContent-Lengthheader no longer
kills the server mid-session. - OSV
querybatchpagination: truncated advisory sets are no longer cached
as authoritative. --save-baselinekeeps stdout pure JSON (status note moved to stderr, the
report is still emitted);quality_scoreis recomputed after
--gate/--min-confidence/--baselinefiltering so the envelope is
internally consistent.- Update-check cache writes atomically (temp file + rename).
Changed
mollify-typescontract enums are#[non_exhaustive](adding a report
kind/category is no longer a breaking Rust change), and the load-bearing
Confidence/Severityorderings are documented and locked by tests.- Docs/CI hygiene:
AGENTS.mdno longer documents a nonexistent
mollify graph --formatflag; the GitLab CI example uses
artifacts:reports:sarif; the advisory-database docs describe the actual
live-by-default behavior;bump-version.shworks on BSD/macOS sed;
Dependabot covers GitHub Actions and Cargo; the report hook can no longer
fail the action on malformed JSON.
Install / upgrade
uvx mollify@0.1.4 audit # or: uv tool install mollify
pip install --upgrade mollify
cargo install mollify-cli # binary: mollifyIf you use --save-baseline in CI, regenerate your baseline after upgrading — fingerprints changed (see Breaking above).
Full changelog: https://github.com/FavioVazquez/mollify/blob/v0.1.4/CHANGELOG.md