Skip to content

build(mise): add qlty for per-function complexity - #131

Draft
iloveitaly wants to merge 13 commits into
masterfrom
cursor/add-qlty-dev-3667
Draft

iloveitaly wants to merge 13 commits into
masterfrom
cursor/add-qlty-dev-3667

Conversation

@iloveitaly

@iloveitaly iloveitaly commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Motivation

Per-function cognitive complexity is the signal we want when reviewing whether a function is hard to follow. Qlty reports that score for Python and TypeScript, and its plugin checks should run with the existing lint recipes.

Celery's task_prerun and task_postrun receivers have to keep the framework's parameter list. Built-in smells cannot be ignored per function, so app/celery.py is excluded from analysis.

Asserts are part of the application. PYTHONOPTIMIZE and python -O strip them, so startup rejects that mode and Bandit no longer flags assert usage.

TODO comments stay in the code. Radarlint python:S1135 is turned off. The two Stripe test client_secret fixtures are fake values and are ignored only on those lines.

The existing fixture random picks and the direnv just runner are grandfathered. Bandit still flags new random and subprocess uses.

The shared "fbc resolved" log event and send_conversion_event's parameter list are grandfathered. Radarlint still flags new duplicated literals and oversized functions.

Description

github:qltysh/qlty is in .config/mise.dev.toml as latest. It uses the repo-wide 14-day release hold. The lock is 0.659.0 (20 Oct 2026), the newest release past that cutoff.

Project config is the standard .qlty/qlty.toml. The CLI has no config-path flag. Python and JavaScript share that file. Per-language smell thresholds go under [language.<name>.smells].

Plugins the existing recipes already run are commented out: ruff and ast-grep (py_lint), eslint (js_lint), actionlint and gitleaks (dev_lint). py_lint runs qlty check --filter bandit,radarlint-python. The js_lint Qlty call is commented out with the eslint plugin.

configure_python raises if sys.flags.optimize is set. Bandit reads .qlty/configs/.bandit and skips B101.

.qlty/configs/radarlint.properties sets radarlint.rules.python.S1135=false. Qlty links that file into the working directory when the radarlint-python plugin runs. B105 stays enabled. The two client_secret assignments in tests/unit/stripe_test.py carry a same-line # nosec B105.

tests/data.py marks the two choice fixture picks with # nosec B311. tests/direnv.py marks the subprocess import with # nosec B404 and the just invocation with # nosec B603, B607. Those rules are not in the global skip list.

app/helpers/facebook.py marks the first "fbc resolved" log line with # NOSONAR for python:S1192, and the *, that opens send_conversion_event with # NOSONAR for python:S107. Radarlint only honors that marker on a line inside the issue range. Those rules stay enabled.

replace_lines_in_files in .copier/bootstrap.py had a docstring that was a Python dict literal. Radarlint python:S125 treated that as commented-out code. The docstring is now one sentence; the parameter type already states the shape.

app/routes/static.py uses INDEX_HTML for the SPA shell and prerender paths. Radarlint python:S1192 flagged the duplicated "index.html" literal.

Screenshots / Test

qlty config validate passes with ruff, ast-grep, eslint, actionlint, and gitleaks commented out.

qlty check --filter bandit,radarlint-python app/routes/errors.py reports no bandit:B101 findings. just --dry-run shows py_lint calling that filter and js_lint no longer calling Qlty.

qlty check --filter radarlint-python .copier/bootstrap.py reports no issues.

qlty check --filter radarlint-python app/routes/static.py no longer reports python:S1192. The three TODO findings on that file are covered by the S1135 disable. tests/routes/static_test.py did not run here: Redis at redis.workspace.orb.local:6379 is unreachable during app import.

qlty check --filter bandit,radarlint-python tests/unit/stripe_test.py app/configuration/lang.py reports no issues. With the properties file moved aside, app/configuration/lang.py still reports python:S1135. An unmarked client_secret assignment still reports bandit:B105.

qlty check --filter bandit tests/data.py tests/direnv.py reports no issues. An unmarked choice([1]) still reports bandit:B311.

qlty check --filter radarlint-python app/helpers/facebook.py reports no issues. An unmarked 14-parameter function and three "fbc resolved" literals still report python:S107 and python:S1192.

Links

Open in Web Open in Cursor 

cursoragent and others added 3 commits November 4, 2026 04:27
Install the Qlty CLI from GitHub releases so function-level cognitive
and cyclomatic scores can be collected during development.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Celery's task_prerun and task_postrun receivers have to keep the
framework parameter list, and built-in smells cannot be ignored per
function. Skip app/celery.py entirely.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Keep qlty.toml with the other lint config. The CLI only reads
.qlty/qlty.toml, so that path is a symlink. py_lint runs the Python
plugins and js_lint runs eslint on web paths.

Co-authored-by: Michael Bianco <mike@mikebian.co>
@cursor
cursor Bot force-pushed the cursor/add-qlty-dev-3667 branch from d74f27d to 7764a2f Compare November 4, 2026 04:31
cursoragent and others added 10 commits November 4, 2026 11:38
The CLI only reads .qlty/qlty.toml, so drop the lint-folder copy and
symlink. Set this tool's minimum_release_age to 0s so latest is not
held by the repo-wide 14-day cutoff.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Drop the per-tool minimum_release_age override. latest now resolves
to 0.659.0, the newest release past the 14-day cutoff.

Co-authored-by: Michael Bianco <mike@mikebian.co>
The CLI reads a single qlty.toml, smell thresholds are per language
in that file, and the lint recipes filter plugins instead of splitting
config.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Startup fails when the interpreter is optimized, so assert statements
stay in the bytecode. Bandit B101 is skipped for that reason.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Ruff, ast-grep, eslint, actionlint, and gitleaks stay in py_lint,
js_lint, and dev_lint. Qlty keeps the plugins those recipes do not run.

Co-authored-by: Michael Bianco <mike@mikebian.co>
…code

The example dict in replace_lines_in_files looked like commented-out Python to python:S125. The type signature already states the shape.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Radarlint S1192 flagged the "index.html" literal repeated on the shell and prerender paths.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Disable python:S1135 in .qlty/configs/radarlint.properties. Mark the two
Stripe client_secret fixtures with nosec B105 so other hardcoded
passwords still report.

Co-authored-by: Michael Bianco <mike@mikebian.co>
B311, B404, B603, and B607 stay enabled. The two fixture pickers and the just runner are marked nosec.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Radarlint S1192 and S107 stay enabled. NOSONAR marks the shared log event and the conversions api parameter list.

Co-authored-by: Michael Bianco <mike@mikebian.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants