fix: JS/Phoenix false-positives in health-report (no_dead_code, boolean ?-suffix)#66
Conversation
Detects statements unreachable because they follow a terminal statement (return/raise/throw/break/continue) within the same indentation scope. Distinguishes genuine dead code from idiomatic early-return guards: a guard's trailing code sits at a shallower indent (outside the block) and is not flagged, while siblings at the same-or-deeper indent after a terminal are. Lines ending with net-open brackets are treated as multi-line expression continuations, not block-level terminals. Line- and indent-based, language-agnostic across brace/keyword-delimited languages. Gives the no_dead_code_after_return behavior the structural signal cosine similarity on aggregate metrics cannot capture.
JS has no `?`-suffix predicate convention — `isActive()`/`hasFoo()` is the idiom, not `active?()`. Removed the JS sample pair so apply-languages drops javascript from the behavior's _languages allowlist (now elixir/python/ruby), and corrected the doc string. Also weaves the new unreachable_code metric scalars into this category (side effect of apply-scalars).
JS early-return guards were flagged as dead code: as a pure cosine classifier the behavior could not tell a guard from genuine unreachable code (near-identical aggregate profiles). Two changes: - 10 good/bad JS sample pairs showcasing guard patterns (DOM hooks, listeners, utils, async) as positive samples, so apply-scalars learns the new unreachable_code metric weight (mean_unreachable_after_terminal_ratio -1.97, the strongest negative scalar in the behavior). - _excludes_languages now also blocks json/xml — data files have no returns and were nonsensically flagged. Recalibration (apply-scalars) re-weaves the unreachable_code scalars across all behaviors; the touched YAMLs reflect that, not behavior changes. Verified against position-db: no_dead_code JS false-positives 4 -> 1, force_graph.js (multi-line return) and package.json (JSON) eliminated. Refs #65
🟠 Code Health: C+ (63/100)
Metric Changes
%%{init: {'theme': 'neutral'}}%%
xychart-beta
title "Code Health Scores"
x-axis ["Readability", "Complexity", "Structure", "Duplication", "Naming", "Magic Numbers", "Combined Metrics"]
y-axis "Score" 0 --> 100
bar [94, 30, 88, 48, 96, 100, 65]
|
🔍 Top Likely Issues (cosine similarity)
🟢 Readability — A (94/100)Codebase averages: flesch_adapted=97.84, fog_adapted=4.82, avg_tokens_per_line=9.54, avg_line_length=35.75
🔴 Complexity — D- (30/100)Codebase averages: difficulty=41.46, effort=237270.59, volume=4072.01, estimated_bugs=1.36
🟢 Structure — A- (88/100)Codebase averages: branching_density=0.14, mean_depth=3.86, avg_function_lines=8.31, max_depth=9.22, max_function_lines=19.97, variance=6.84, avg_param_count=1.16, max_param_count=2.06
🟠 Duplication — C- (48/100)Codebase averages: redundancy=0.59, bigram_repetition_rate=0.54, trigram_repetition_rate=0.37
🟢 Naming — A (96/100)Codebase averages: entropy=0.89, mean=6.64, variance=18.82, avg_sub_words_per_id=1.17
🟢 Magic Numbers — A (100/100)Codebase averages: density=0.00
🔴 Combined Metrics — D (65/100)
🔴 Code Smells — D- (25/100)
🟡 Consistency — B+ (81/100)
🔴 Dependencies — E+ (19/100)
🟡 Documentation — B+ (83/100)
🟢 Error Handling — A- (91/100)
🟠 File Structure — C- (48/100)
🟡 Function Design — B+ (81/100)
🟢 Naming Conventions — A- (90/100)
🔴 Scope And Assignment — D- (28/100)
🟡 Testing — B+ (83/100)
🟢 Type And Value — A- (89/100)
🟡 Variable Naming — B (74/100)
|
kind: refactoring-tasks
|

Was & warum
health-reporterzeugte gegen Phoenix-Repos mit JS-Assets eine Reihe von False Positives in den Handlungsempfehlungen (entdeckt beim Lauf gegenposition-db, s. #65). Drei Ursachen, drei Fixes:1.
no_dead_code_after_returnflaggte idiomatische JS early-return-Guards als dead codeAls reiner cosine-Klassifizierer konnte das Behavior
if (!x) return;(Guard) nicht von echtem unreachable code unterscheiden — die Aggregat-Metrik-Profile sind nahezu identisch. Sample-Tuning allein war empirisch asymptotisch (Cosine -0.41 → -0.34 über 3→10 Sample-Paare, Schwelle nie erreicht).Fix: Neue
UnreachableCode-File-Metrik (feat-Commit) liefert das strukturelle Signal, das cosine fehlt — sie misst, ob nach einem terminalen Statement (return/raise/throw/break/continue) Code auf gleichem-oder-tieferem Indent folgt. Guards → 0, dead code → >0. Mehrzeiligereturn (…)-Ausdrücke werden über Klammer-Bilanz korrekt als Fortsetzung erkannt, nicht als dead code. Nachapply-scalarsbekammean_unreachable_after_terminal_ratiodas stärkste negative Gewicht (-1.97) im Behavior.Zusätzlich: 10 good/bad JS-Sample-Paare (Guard-Patterns) als Trainingsbasis, und
_excludes_languagesblockt jetzt auchjson/xml(Datenfiles haben keine returns).2.
boolean_function_has_question_markfeuerte auf JSDas
?-Suffix ist eine Elixir/Ruby-Konvention — JS nutztisActive()/hasFoo(). JavaScript aus der_languages-Allowlist entfernt (JS-Samples gelöscht →apply-languageszieht das automatisch), doc korrigiert.3. Re-Kalibrierung
apply-scalarswebt die neue Metrik in alle Behaviors ein und re-trainiert die Gewichte über die Sample-Basis. Die berührten YAMLs (consistency,error_handling,scope_and_assignment,type_and_value,variable_naming) spiegeln das — keine bewussten Behavior-Änderungen, sondern die unvermeidliche Folge einer neuen globalen Metrik.Verifikation (gegen
position-db, full-repo)boolean_function_has_question_markauf JSforce_graph.js(multi-line return)package.json(JSON)mix credo --strict, no issuesBekannte Grenze
Ein
no_dead_code-FP bleibt:sticky_offset.js. Die neue Metrik gibt für diese Datei korrekt0zurück — das Behavior feuert dennoch knapp, weil das additive cosine-Modell das starke-1.97-Signal von vielen schwachen anderen Metriken überstimmen lässt. Das ist eine Modell-Grenze, kein Metrik-Bug. Verbleibende FPs (sticky_offsetcosine,registry_mass.exboolean-?,name_contains_and) als Follow-up unter #65.Refs #65