Commit Graph

5 Commits

Author SHA1 Message Date
Teknium
fccb46ac48 fix(code-health): measure every Python unit's real end line
compare.py's cross-file move matching slices the origin function by its end line;
without it the engine estimated the end from the line count. Pass ast's end_lineno through.
2026-10-04 21:29:39 -07:00
Teknium
6ca1395ee0 fix(code-health): NESTING counts else: + indented if as nesting
JoaoMarcos44 (inline py_structure.py:49, review 5408439923; reproduced by
andrexibiza): nesting_depth() treated any If that sits alone in an orelse as
an `elif`, but `else:\n    if b:` produces the same AST. Seven physical
`else: if` levels measured 1 (adding an inert `pass` made them measure 7), so
the gate depended on an irrelevant statement rather than on indentation.

A real `elif` If node starts at the `elif` keyword, i.e. at its `if`'s
column; an `if` nested under `else:` must be indented deeper (a compound
statement cannot follow `else:` on the same line). Only the former keeps the
depth. Seven physical levels now measure 7 with or without the inert pass
and block; a genuine seven-arm elif chain stays at 1 and passes.
2026-10-04 21:29:39 -07:00
Teknium
ae60f7ccc1 fix(code-health): recursive-rename normalization honours nested scopes (F15)
andrexibiza's F15 (changed form, inline on py_rules.py:250): body_hash()
disabled self-reference normalization whenever the textual dump contained
`arg('<name>'` or `Name('<name>', Store())` anywhere in the subtree, so an
unrelated nested `def identity(legacy)` made a pure recursive rename of
`legacy` lose its cap (FUNC_LINES 302 > target 300 as new code). Even with
the check removed, the string replace would also have blanked the nested
function's own `return legacy`, which refers to its parameter.

References are now resolved lexically: only bindings in the function's own
scope (params, assignments, nested def/class names, imports, global/nonlocal,
except/match captures) disable normalization, and a nested def/lambda/class/
comprehension that binds the name keeps its own reads. The matched Name /
Attribute nodes are blanked in place for the dump and restored. Controls: a
rename that leaves the old self-call is different code, an own-scope rebinding
is not a self-reference, a closure's call to its enclosing function is.
2026-10-04 21:29:39 -07:00
teknium1
cf0755f947 fix(code-health): occurrence provenance, alias-aware rules, strict file cap (round 4)
Fourth review round (F11-F18, N1-N4, kvnloo, Adolanium):

- Hits are old only where the base occurrence survives: a line the comment- and
  whitespace-blind line diff keeps (blame semantics), or anything inside a unit whose
  body is unchanged. An identical violation moved past unrelated code is new; removing
  an allow comment or re-indenting is not. Hit fingerprints exclude comments (N4).
- Inline allows cover their own line; only a comment-only line covers the next (N3).
- Rules read a copy of the AST with import aliases resolved, so `sp.run`,
  `from subprocess import run as execute`, aliased threading/asyncio/load_config are
  seen (F12); deadline helpers must resolve to the real asyncio API, and
  communicate()/wait() are judged only on receivers holding a spawned process (F13);
  infinite deadlines are unbounded (F14).
- HERMES_* writes introduce a name too (N1); bool(os.environ[...]) is HX010 (N2);
  HX009 needs the isinstance subject to come from gather(return_exceptions=True).
- Recursive self-references are normalised out of body hashes, so a recursive rename
  keeps its cap, py and ts (F15). Decorators belong to their function's scope and
  classes are matchable units, so their hits follow proven moves (F16).
- An over-cap file may not grow (F17, Teknium: strict improvement-only).
- A file the checker cannot parse or measure (ast/ruff/TS parse error) fails.
- scripts/check: --staged always lints an index snapshot (ignored files are invisible
  to git status, not to guards) (F18); create the --prefix dir for Git for Windows
  (F11); --only builds just the selected jobs, so it needs no ruff for non-ruff checks.
- CODEOWNERS covers scripts/check + scripts/code_health; frontend-only PRs skip the
  Python-only lint jobs again and run only the ratchet.
2026-10-04 21:29:39 -07:00
teknium1
4156715960 feat: code-health ratchet with per-unit caps for Python and TypeScript
scripts/code_health measures the files a change touches on both sides
(base revision and head) and fails when:
- a function or file over target grows past its own value on the base
  (CC 20, 300 own lines, nesting 6, file 2,000 lines);
- a new function or file is over target;
- a pattern rule gains a fingerprint (rule + enclosing function + line
  text), so fixing one violation never buys another.

Code that moves keeps its cap: functions match by name-independent body
hash, pattern hits by (rule, line text) once their function has left the
old place. Nested functions are their own units, so a closure edit never
counts against the hook or function around it. Python CC is ruff's C901
minus the direct nested defs; TypeScript is parsed with the lockfile's
typescript (no type check).

Rules: swallowed exceptions (S110, BLE001, E722), correctness (F821/F823/
F811/F841, RUF006), hangs (ASYNC230/240, S113, subprocess/urlopen without
timeout, sync config I/O in async def, get_event_loop, gather results
checked against Exception), profile scope (hardcoded ~/.hermes, a new
HERMES_* env var, UnscopedSecretError -> os.getenv, import-time capture,
raw threading.Thread, P05/P06 from profile_scope_patterns.json), argv
process identity, bool() of an env string, and if/elif ladders.

Growth of a file already over 2,000 lines is advisory: replaying 300
merged PRs, a blocking file-size ratchet would have stopped 46 of them
before the 119 over-target files are split. Everything else blocks; the
same replay finds 18 PRs that would have needed a change, every finding
a real hit for its rule.

No baseline file: base is measured on the fly, so an improvement that
lands on main is the new cap at once and parallel PRs never conflict on
ratchet state. `# health: allow <RULE> -- <why>` is the reviewed escape.
2026-10-04 21:29:39 -07:00