# Hermes WebUI — Python tooling config.
#
# This project is NOT a packaged distribution. The app is a plain Python + vanilla
# JavaScript server with no build step and no bundler (see AGENTS.md / README). This
# file exists only to configure dev tooling — currently ruff, used as a curated,
# forward-looking lint gate over the Python codebase. There is intentionally no
# [build-system] section: nothing pip-installs this directory.
#
# The ruff gate is the Python twin of the ESLint runtime guard (package.json
# `lint:runtime` + eslint.runtime-guard.config.mjs + tests/test_static_js_runtime_lint.py).
# It is enforced on NEW/CHANGED code only — see scripts/ruff_lint.py and TESTING.md
# > "Python lint gate (ruff)". The existing tree carries a cosmetic backlog (mostly
# unused-import F401) that is deliberately NOT reformatted here; cleaning it is a
# separate, maintainer-run, safe-fixes-only decision (tracked in #3273).

[tool.ruff]
# Match the Python versions exercised in CI (tests.yml matrix: 3.11–3.13).
target-version = "py311"
# Keep the linter scoped to the application + tests; never crawl vendored or
# generated trees. (These are belt-and-suspenders; the gate passes explicit files.)
extend-exclude = [
    "node_modules",
    "static",
    ".git",
    "scripts/windows",
    "scripts/wsl",
]

[tool.ruff.lint]
# Curated, correctness-leaning ruleset — high signal, low noise. We deliberately do
# NOT enable the pure-style families (E1/E2/E5/E7 line-length & whitespace) so the
# gate never demands a whitespace reformat of existing code.
#
#   E9  — syntax / IO / runtime errors (E999 etc). The whole tree is already clean
#         of these and the in-suite test (tests/test_ruff_forward_lint.py) keeps it
#         that way across every shard.
#   F   — pyflakes: unused imports (F401), unused/undefined names (F841/F821),
#         redefinitions (F811), f-strings with no placeholders (F541). The most
#         valuable family for keeping NEW code clean.
#   B   — flake8-bugbear: genuine latent-bug shapes — mutable default args (B006),
#         raise-without-from (B904), loop-variable capture in closures (B023),
#         zip-without-strict (B905). This is where the real future-regression-
#         prevention value lives.
select = ["E9", "F", "B"]

# No global `ignore` of F401/F841/etc. The existing-tree backlog is handled by
# line-scoping the gate to changed lines (scripts/ruff_lint.py), NOT by globally
# disabling the rules — disabling them would blind the gate to the single most
# common new-code defect (a stray unused import). Forward enforcement of the full
# curated set is the whole point.

[tool.ruff.lint.per-file-ignores]
# Tests legitimately use `assert False` as an explicit failure marker (B011) and
# occasionally shadow loop vars in table-driven cases (B007); that's idiomatic in a
# test suite and not a production-code risk.
"tests/**" = ["B011", "B007"]
