perf(block-impact): byte-exact block slice enables subtractive LOO#62
Conversation
The leave-one-out path rebuilt the file-minus-block from normalized structural tokens, which collapse inter-token whitespace, round indentation to 2-space units, and map non-ASCII to spaces. So the reconstructed string diverged from the source, and any subtractive metric (baseline minus block) computed against an original-file baseline diverged too — the latent separator_counts bug (0/50 matches), which had been worked around by removing its analyze_loo/2. Fix the root cause: slice_without_original/2 cuts the block out of the original bytes using the first/last token's line+col, so block_content is the verbatim source span and reconstructed is the original file minus that span. Cuts fall on token boundaries, so counts subtract exactly. - analyze_loo/2 contract takes a shared block FileContext (built once per node) instead of a raw string, so subtractive metrics extract identifiers/tokens via the same pipeline as the baseline. - separator_counts and vowel_density go subtractive again, now correct: ~23ms and ~7ms per node drop to ~0.1ms. Verified by the subtractive_loo goldfile guard against real nested sample blocks. - The big LOO costs (punctuation densities, halstead n1/n2, ngram, line-based) stay on the re-analyze fallback: they are set-based or context-dependent at block boundaries and cannot subtract exactly. Measured on a 1/10 subset of a large repo via --telemetry: analyze_file per node 196ms -> 180ms; report output byte-identical except timestamp.
🟠 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, 31, 88, 48, 96, 100, 65]
|
🔍 Top Likely Issues (cosine similarity)
🟢 Readability — A (94/100)Codebase averages: flesch_adapted=97.81, fog_adapted=4.82, avg_tokens_per_line=9.52, avg_line_length=35.74
🔴 Complexity — D- (31/100)Codebase averages: difficulty=41.25, effort=234200.38, volume=4045.56, estimated_bugs=1.35
🟢 Structure — A- (88/100)Codebase averages: branching_density=0.14, mean_depth=3.86, avg_function_lines=8.30, max_depth=9.21, max_function_lines=19.95, variance=6.85, avg_param_count=1.16, max_param_count=2.05
🟠 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.65, 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- (92/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
Behebt die Wurzel des gescheiterten subtraktiven LOO-Pfads und macht separator_counts + vowel_density wieder subtraktiv (korrekt).
Wurzelproblem
Der LOO-Pfad baute
file-minus-blockaus normalisierten structural tokens — die kollabieren inter-token-Whitespace, runden Indent auf 2-Space-Units und mappen Non-ASCII auf Spaces. Der reconstructed string wich also vom Source ab, und jede subtraktive Metrik (baseline ⊖ block) gegen eine Original-File-Baseline driftete mit. Das war der latente separator_counts-Bug (0/50 matches), der durch Entfernen vonanalyze_loo/2umgangen worden war.Fix
slice_without_original/2schneidet den Block aus den Original-Bytes via line+col des ersten/letzten Tokens.block_contentist damit der verbatim Source-Span,reconstructeddas Original minus diesem Span. Schnitte fallen auf Token-Grenzen → counts subtrahieren exakt.analyze_loo/2nimmt einen geteilten Block-FileContext(einmal pro Node gebaut) statt eines rohen Strings → subtraktive Metriken extrahieren identifiers/tokens über dieselbe Pipeline wie die Baseline.subtractive_loo-Goldfile-Guard gegen reale verschachtelte Sample-Blocks.Messung (1/10-Subset, --telemetry)
analyze_filepro Node: 196ms → 180ms (~8% head-LOO)Ehrlicher Scope
Der subtraktive Hebel ist physisch auf ~8% begrenzt — die großen Posten (ctx-build, punctuation, halstead, ngram) sind mathematisch nicht subtraktiv. Dieser PR ist der saubere, korrekte Anteil + das Fundament (
slice_without_original, geteilter block_ctx) für spätere Metriken (z.B. entropy via baseline-count-Cache). Der echte Laufzeit-Win kam aus #61 (base-snapshot).