ScanOrchestrator.java

1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright 2026 Egothor
3
// Copyright 2026 Accenture
4
package org.egothor.methodatlas.command;
5
6
import java.io.IOException;
7
import java.nio.file.Path;
8
import java.util.ArrayList;
9
import java.util.LinkedHashMap;
10
import java.util.List;
11
import java.util.Map;
12
import java.util.Optional;
13
import java.util.logging.Level;
14
import java.util.logging.Logger;
15
import java.util.stream.Collectors;
16
17
import org.egothor.methodatlas.AiCacheEntry;
18
import org.egothor.methodatlas.AiCacheStore;
19
import org.egothor.methodatlas.AiResultCache;
20
import org.egothor.methodatlas.emit.ClassificationOverride;
21
import org.egothor.methodatlas.CliConfig;
22
import org.egothor.methodatlas.emit.CompositeTestMethodSink;
23
import org.egothor.methodatlas.emit.TestMethodSink;
24
import org.egothor.methodatlas.ai.AiClassSuggestion;
25
import org.egothor.methodatlas.ai.AiMethodSuggestion;
26
import org.egothor.methodatlas.ai.AiOptions;
27
import org.egothor.methodatlas.ai.AiSuggestionEngine;
28
import org.egothor.methodatlas.ai.AiSuggestionException;
29
import org.egothor.methodatlas.ai.CredentialTriageVerdict;
30
import org.egothor.methodatlas.ai.PromptBuilder;
31
import org.egothor.methodatlas.ai.SuggestionLookup;
32
import org.egothor.methodatlas.api.DiscoveredMethod;
33
import org.egothor.methodatlas.api.TestDiscovery;
34
import org.egothor.methodatlas.api.TestDiscoveryConfig;
35
36
/**
37
 * Orchestrates the scan-and-emit loop that every command mode is built around.
38
 *
39
 * <p>
40
 * Each {@link Command} mode varies in three places — output format, per-record
41
 * sink behaviour, and whether records stream or buffer — but they all share
42
 * the same core sequence:
43
 * </p>
44
 * <ol>
45
 *   <li>Load all configured {@link TestDiscovery} providers.</li>
46
 *   <li>For each scan root, run every provider, merge their results, and
47
 *       group methods by class.</li>
48
 *   <li>For each class, optionally consult the AI engine through a layered
49
 *       cache + override lookup.</li>
50
 *   <li>Forward each method record to the supplied sink.</li>
51
 *   <li>Close all providers.</li>
52
 * </ol>
53
 *
54
 * <p>
55
 * This class owns that sequence. Commands compose it with a
56
 * {@link PluginLoader} (passed in at construction) and configure the
57
 * per-record sink, AI runtime, and content-hash policy at call time.
58
 * </p>
59
 *
60
 * <h2>API shape</h2>
61
 *
62
 * <p>
63
 * Two entry points serve the two common patterns:
64
 * </p>
65
 * <ul>
66
 *   <li>{@link #scan} manages the provider lifecycle internally; it is the
67
 *       right call for SARIF, JSON, and GitHub-annotation modes that buffer
68
 *       or emit unconditionally.</li>
69
 *   <li>{@link #runDiscovery} processes a single root against pre-loaded
70
 *       providers; it is the right call for CSV and plain-text modes that
71
 *       compute per-root metadata (such as the {@code source_root} column)
72
 *       before forwarding records.</li>
73
 * </ul>
74
 *
75
 * <p>
76
 * The apply-tags flow has its own shape: {@link #collectMethodsByFile}
77
 * groups discovered methods by source file (the caller owns the provider
78
 * lifecycle so it can read each provider's
79
 * {@link TestDiscovery#hadErrors()} afterwards), and
80
 * {@link #gatherAiSuggestionsForFile} resolves AI suggestions for one
81
 * file at a time.
82
 * </p>
83
 *
84
 * <h2>Thread safety</h2>
85
 *
86
 * <p>
87
 * This class is thread-safe. The injected {@link PluginLoader} is
88
 * thread-safe and {@link java.util.ServiceLoader} resolution is
89
 * idempotent; nothing else is shared between calls.
90
 * </p>
91
 *
92
 * @see PluginLoader
93
 * @see AiRuntime
94
 * @see Command
95
 * @since 1.0.0
96
 */
97
public final class ScanOrchestrator {
98
99
    private static final Logger LOG = Logger.getLogger(ScanOrchestrator.class.getName());
100
101
    private final PluginLoader pluginLoader;
102
103
    /**
104
     * Optional secondary sink that observes every record alongside the
105
     * command's primary sink. {@code null} means no fan-out.
106
     */
107
    private final TestMethodSink extraSink;
108
109
    /**
110
     * Creates a new orchestrator with no extra sink.
111
     *
112
     * @param pluginLoader plugin loader used by {@link #scan} and
113
     *                     {@link #collectMethodsByFile}; must not be
114
     *                     {@code null}
115
     */
116
    public ScanOrchestrator(PluginLoader pluginLoader) {
117
        this(pluginLoader, Optional.empty());
118
    }
119
120
    /**
121
     * Creates a new orchestrator that fans out every record to {@code extraSink}
122
     * in addition to the command's primary sink.
123
     *
124
     * <p>
125
     * The extra sink, when present, is composed with the primary sink at the
126
     * {@link #runDiscovery} boundary so every command mode (SARIF, JSON, CSV,
127
     * GitHub annotations) sees the same fan-out automatically. When
128
     * {@code extraSink} is {@link Optional#empty()} the orchestrator behaves
129
     * identically to the legacy single-sink constructor.
130
     * </p>
131
     *
132
     * @param pluginLoader plugin loader used by {@link #scan} and
133
     *                     {@link #collectMethodsByFile}; must not be
134
     *                     {@code null}
135
     * @param extraSink    optional secondary sink invoked in addition to the
136
     *                     command-supplied primary sink; must not be
137
     *                     {@code null} (use {@link Optional#empty()})
138
     */
139
    public ScanOrchestrator(PluginLoader pluginLoader, Optional<TestMethodSink> extraSink) {
140
        this.pluginLoader = pluginLoader;
141
        this.extraSink = extraSink.orElse(null);
142
    }
143
144
    /**
145
     * Scans every configured root, forwarding each discovered test method to
146
     * {@code sink}. Loads and closes the {@link TestDiscovery} providers
147
     * internally so callers do not need to manage the lifecycle.
148
     *
149
     * @param roots           source roots to scan; must not be {@code null}
150
     * @param cliConfig       full parsed CLI configuration
151
     * @param discoveryConfig discovery configuration forwarded to providers
152
     * @param aiEngine        AI engine providing suggestions; may be
153
     *                        {@code null} when AI is disabled
154
     * @param sink            receiver of discovered test method records
155
     * @param override        human classification overrides
156
     * @param aiCache         AI result cache
157
     * @return {@code 0} if all files were processed successfully, {@code 1}
158
     *         if any file produced a parse or processing error
159
     * @throws IOException if traversing a file tree fails
160
     * @since 1.0.0
161
     */
162
    public int scan(List<Path> roots, CliConfig cliConfig, TestDiscoveryConfig discoveryConfig,
163
            AiSuggestionEngine aiEngine, TestMethodSink sink,
164
            ClassificationOverride override, AiResultCache aiCache) throws IOException {
165 1 1. scan : replaced int return with 0 for org/egothor/methodatlas/command/ScanOrchestrator::scan → SURVIVED
        return scan(roots, cliConfig, discoveryConfig, aiEngine, sink, override, aiCache, null);
166
    }
167
168
    /**
169
     * Scans every configured root like
170
     * {@link #scan(List, CliConfig, TestDiscoveryConfig, AiSuggestionEngine, TestMethodSink, ClassificationOverride, AiResultCache)},
171
     * additionally folding credential triage into each per-class AI call when
172
     * {@code secretCtx} is supplied, so the class source is sent to the provider
173
     * once for both classification and triage.
174
     *
175
     * @param roots           source roots to scan; must not be {@code null}
176
     * @param cliConfig       full parsed CLI configuration
177
     * @param discoveryConfig discovery configuration forwarded to providers
178
     * @param aiEngine        AI engine providing suggestions; may be {@code null}
179
     * @param sink            receiver of discovered test method records
180
     * @param override        human classification overrides
181
     * @param aiCache         AI result cache
182
     * @param secretCtx       credential-triage context, or {@code null} to disable
183
     *                        the fold (classification only)
184
     * @return {@code 0} if all files were processed successfully, {@code 1} otherwise
185
     * @throws IOException if traversing a file tree fails
186
     * @since 4.1.0
187
     */
188
    public int scan(List<Path> roots, CliConfig cliConfig, TestDiscoveryConfig discoveryConfig,
189
            AiSuggestionEngine aiEngine, TestMethodSink sink,
190
            ClassificationOverride override, AiResultCache aiCache,
191
            CredentialTriageContext secretCtx) throws IOException {
192
        List<TestDiscovery> providers = pluginLoader.loadProviders(discoveryConfig);
193
        boolean hadErrors = false;
194
        // Accumulates one cache entry per processed class across all roots; written
195
        // once at the end when -ai-cache-out is set.
196
        Map<String, AiCacheEntry> cacheAll =
197 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → KILLED
                cliConfig.aiCacheOut() != null ? new LinkedHashMap<>() : null;
198
        try {
199
            for (Path root : roots) {
200
                DiscoveryResult result = runDiscoveryInternal(root, providers, cliConfig.aiOptions(),
201
                        aiEngine, sink, cliConfig.contentHash(), override, aiCache, secretCtx);
202 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → KILLED
                if (result.hadErrors()) {
203
                    hadErrors = true;
204
                }
205 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → KILLED
                if (cacheAll != null) {
206 1 1. scan : removed call to java/util/Map::putAll → NO_COVERAGE
                    cacheAll.putAll(result.cacheEntries());
207
                }
208
            }
209
        } finally {
210 1 1. scan : removed call to org/egothor/methodatlas/command/PluginLoader::closeAll → SURVIVED
            pluginLoader.closeAll(providers);
211
        }
212 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → KILLED
        if (cacheAll != null) {
213 1 1. scan : removed call to org/egothor/methodatlas/AiCacheStore::write → NO_COVERAGE
            AiCacheStore.write(cliConfig.aiCacheOut(), cacheAll.values());
214
        }
215 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → KILLED
        return hadErrors ? 1 : 0;
216
    }
217
218
    /**
219
     * Runs all configured {@link TestDiscovery} providers on {@code root},
220
     * merges their results, orchestrates AI analysis per class, and forwards
221
     * each method record to {@code sink}.
222
     *
223
     * <p>
224
     * Providers are passed in pre-loaded; callers manage the lifecycle
225
     * (typically through {@link PluginLoader#closeAll(List)} in a
226
     * {@code finally} block) so that they can share one provider list across
227
     * multiple roots while still computing per-root metadata before each
228
     * call.
229
     * </p>
230
     *
231
     * @param root               directory to scan
232
     * @param providers          list of pre-configured discovery providers
233
     * @param aiOptions          AI configuration for the current run
234
     * @param aiEngine           AI engine, or {@code null} when AI is disabled
235
     * @param sink               receiver of discovered test method records
236
     * @param contentHashEnabled whether to include the class content hash in
237
     *                           emitted records
238
     * @param override           human classification overrides
239
     * @param aiCache            AI result cache
240
     * @return {@code true} if any provider encountered a parse or processing
241
     *         error
242
     * @throws IOException if traversing the file tree fails
243
     * @since 1.0.0
244
     */
245
    public boolean runDiscovery(Path root, List<TestDiscovery> providers,
246
            AiOptions aiOptions, AiSuggestionEngine aiEngine, TestMethodSink sink,
247
            boolean contentHashEnabled, ClassificationOverride override,
248
            AiResultCache aiCache) throws IOException {
249 2 1. runDiscovery : replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED
2. runDiscovery : replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED
        return runDiscovery(root, providers, aiOptions, aiEngine, sink, contentHashEnabled,
250
                override, aiCache, null);
251
    }
252
253
    /**
254
     * Runs discovery and AI analysis for one root like
255
     * {@link #runDiscovery(Path, List, AiOptions, AiSuggestionEngine, TestMethodSink, boolean, ClassificationOverride, AiResultCache)},
256
     * additionally folding credential triage into each per-class AI call when
257
     * {@code secretCtx} is supplied.
258
     *
259
     * @param root               directory to scan
260
     * @param providers          pre-configured discovery providers
261
     * @param aiOptions          AI configuration for the current run
262
     * @param aiEngine           AI engine, or {@code null} when AI is disabled
263
     * @param sink               receiver of discovered test method records
264
     * @param contentHashEnabled whether to include the class content hash
265
     * @param override           human classification overrides
266
     * @param aiCache            AI result cache
267
     * @param secretCtx          credential-triage context, or {@code null} to disable the fold
268
     * @return {@code true} if any provider encountered an error
269
     * @throws IOException if traversing the file tree fails
270
     * @since 4.1.0
271
     */
272
    public boolean runDiscovery(Path root, List<TestDiscovery> providers,
273
            AiOptions aiOptions, AiSuggestionEngine aiEngine, TestMethodSink sink,
274
            boolean contentHashEnabled, ClassificationOverride override,
275
            AiResultCache aiCache, CredentialTriageContext secretCtx) throws IOException {
276 2 1. runDiscovery : replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED
2. runDiscovery : replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED
        return runDiscoveryInternal(root, providers, aiOptions, aiEngine, sink, contentHashEnabled,
277
                override, aiCache, secretCtx).hadErrors();
278
    }
279
280
    /**
281
     * Body of {@link #runDiscovery(Path, List, AiOptions, AiSuggestionEngine, TestMethodSink,
282
     * boolean, ClassificationOverride, AiResultCache, CredentialTriageContext)} that also returns one
283
     * {@link AiCacheEntry} per processed class (cache hits preserved, misses freshly computed) so the
284
     * caller can persist the unified AI result cache.
285
     *
286
     * @return the error flag plus the per-class cache entries keyed by content hash
287
     */
288
    @SuppressWarnings("PMD.CloseResource") // providers are owned by the caller; this method does not close them
289
    private DiscoveryResult runDiscoveryInternal(Path root, List<TestDiscovery> providers,
290
            AiOptions aiOptions, AiSuggestionEngine aiEngine, TestMethodSink sink,
291
            boolean contentHashEnabled, ClassificationOverride override,
292
            AiResultCache aiCache, CredentialTriageContext secretCtx) throws IOException {
293
294
        TestMethodSink effectiveSink = wrapWithExtraSink(sink);
295
        List<DiscoveredMethod> methods = new ArrayList<>();
296
        boolean hadErrors = false;
297
        for (TestDiscovery provider : providers) {
298 1 1. runDiscoveryInternal : removed call to java/util/stream/Stream::forEach → KILLED
            provider.discover(root).forEach(methods::add);
299 2 1. runDiscoveryInternal : removed conditional - replaced equality check with true → KILLED
2. runDiscoveryInternal : removed conditional - replaced equality check with false → KILLED
            if (provider.hadErrors()) {
300
                hadErrors = true;
301
            }
302
        }
303
304
        Map<String, List<DiscoveredMethod>> byClass = methods.stream()
305
                .collect(Collectors.groupingBy(DiscoveredMethod::fqcn,
306
                        LinkedHashMap::new, Collectors.toList()));
307
308
        AiRuntime ai = new AiRuntime(aiOptions, aiEngine, override, aiCache);
309
        // The prompt-catalogue signature gates cache reuse and tags written entries;
310
        // only meaningful when AI is enabled.
311 2 1. runDiscoveryInternal : removed conditional - replaced equality check with false → SURVIVED
2. runDiscoveryInternal : removed conditional - replaced equality check with true → KILLED
        String promptSignature = aiEngine == null ? null : aiOptions.promptTemplates().signature();
312
313
        Map<String, AiCacheEntry> cacheEntries = new LinkedHashMap<>();
314
        for (Map.Entry<String, List<DiscoveredMethod>> entry : byClass.entrySet()) {
315 1 1. runDiscoveryInternal : removed call to org/egothor/methodatlas/command/ScanOrchestrator::processClass → KILLED
            processClass(entry, ai, promptSignature, contentHashEnabled, secretCtx,
316
                    effectiveSink, cacheEntries);
317
        }
318
319 1 1. runDiscoveryInternal : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::runDiscoveryInternal → KILLED
        return new DiscoveryResult(hadErrors, cacheEntries);
320
    }
321
322
    /**
323
     * Classifies one class (consulting the cache), feeds its methods to {@code effectiveSink}, and —
324
     * when AI produced a cacheable answer with a content hash — records a cache entry.
325
     *
326
     * @param entry           one class's discovered methods, keyed by FQCN
327
     * @param ai              AI runtime (engine, override, cache)
328
     * @param promptSignature current prompt-catalogue signature, or {@code null} when AI is disabled
329
     * @param contentHashEnabled whether the emitted records carry the content hash
330
     * @param secretCtx       credential-triage context, or {@code null}
331
     * @param effectiveSink   sink receiving the per-method records
332
     * @param cacheEntries    accumulator to record the cacheable answer into, keyed by content hash
333
     * @throws IOException if the sink fails to record a method
334
     */
335
    private void processClass(Map.Entry<String, List<DiscoveredMethod>> entry, AiRuntime ai,
336
            String promptSignature, boolean contentHashEnabled, CredentialTriageContext secretCtx,
337
            TestMethodSink effectiveSink, Map<String, AiCacheEntry> cacheEntries) throws IOException {
338
        String fqcn = entry.getKey();
339
        List<DiscoveredMethod> classMethods = entry.getValue();
340
341
        // groupingBy never produces an empty value list, so the first method is always
342
        // present; it is the representative carrying the class-level fields (source
343
        // content and file stem are identical across a class's methods).
344
        DiscoveredMethod representative = classMethods.get(0);
345
        String classSource = representative.sourceContent().get().orElse(null);
346
347
        // The content hash is needed to read the cache, to emit the hash column, and to
348
        // key a written cache entry — so compute it whenever any of those apply.
349 6 1. processClass : removed conditional - replaced equality check with false → SURVIVED
2. processClass : removed conditional - replaced equality check with true → SURVIVED
3. processClass : removed conditional - replaced equality check with false → SURVIVED
4. processClass : removed conditional - replaced equality check with true → SURVIVED
5. processClass : removed conditional - replaced equality check with true → SURVIVED
6. processClass : removed conditional - replaced equality check with false → KILLED
        String lookupHash = (contentHashEnabled || ai.cache().isActive() || ai.engine() != null)
350 2 1. processClass : removed conditional - replaced equality check with false → SURVIVED
2. processClass : removed conditional - replaced equality check with true → KILLED
                && classSource != null
351
                ? ContentHasher.hashClass(classSource) : null;
352 2 1. processClass : removed conditional - replaced equality check with true → SURVIVED
2. processClass : removed conditional - replaced equality check with false → KILLED
        String outputHash = contentHashEnabled ? lookupHash : null;
353
354
        String fileStem = representative.fileStem();
355
        List<String> methodNames = classMethods.stream().map(DiscoveredMethod::method).toList();
356
        List<PromptBuilder.TargetMethod> targetMethods = classMethods.stream()
357
                .map(ScanOrchestrator::toTargetMethod)
358
                .toList();
359
360
        Resolved resolved = resolveSuggestionLookup(fileStem, fqcn, classSource, methodNames,
361
                targetMethods, ai, lookupHash, promptSignature, secretCtx);
362
        SuggestionLookup suggestions = resolved.lookup();
363
364
        for (DiscoveredMethod m : classMethods) {
365 1 1. processClass : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
            effectiveSink.record(m.fqcn(), m.method(), m.beginLine(), m.loc(), outputHash,
366
                    m.tags(), m.displayName(),
367
                    suggestions.find(m.method()).orElse(null));
368
        }
369
370 4 1. processClass : removed conditional - replaced equality check with false → SURVIVED
2. processClass : removed conditional - replaced equality check with true → SURVIVED
3. processClass : removed conditional - replaced equality check with false → SURVIVED
4. processClass : removed conditional - replaced equality check with true → SURVIVED
        if (resolved.cacheable() != null && lookupHash != null) {
371
            cacheEntries.put(lookupHash, new AiCacheEntry(lookupHash, promptSignature, resolved.cacheable()));
372
        }
373
    }
374
375
    /**
376
     * Wraps {@code primary} with {@link CompositeTestMethodSink} when an
377
     * extra sink was supplied to the constructor; returns {@code primary}
378
     * unchanged otherwise.
379
     *
380
     * @param primary command-supplied primary sink
381
     * @return effective sink to feed during this discovery pass
382
     */
383
    private TestMethodSink wrapWithExtraSink(TestMethodSink primary) {
384 2 1. wrapWithExtraSink : removed conditional - replaced equality check with false → KILLED
2. wrapWithExtraSink : removed conditional - replaced equality check with true → KILLED
        if (extraSink == null) {
385 1 1. wrapWithExtraSink : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::wrapWithExtraSink → KILLED
            return primary;
386
        }
387 1 1. wrapWithExtraSink : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::wrapWithExtraSink → KILLED
        return new CompositeTestMethodSink(primary, extraSink);
388
    }
389
390
    /**
391
     * Collects all discovered methods from every configured root, keyed by
392
     * source-file path. Methods whose {@link DiscoveredMethod#filePath()} is
393
     * {@code null} are silently skipped.
394
     *
395
     * <p>
396
     * Providers are passed in pre-loaded so the caller can read each
397
     * provider's {@link TestDiscovery#hadErrors()} after the call and decide
398
     * how to propagate the exit code; the caller also owns closing them.
399
     * </p>
400
     *
401
     * @param roots     scan roots; must not be {@code null}
402
     * @param providers configured and already-loaded discovery providers;
403
     *                  must not be {@code null}
404
     * @return mutable map from source-file path to the methods found in that
405
     *         file; insertion order matches discovery order
406
     * @throws IOException if directory traversal fails for any root
407
     */
408
    @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops",
409
            "PMD.CloseResource"}) // providers are owned by the caller
410
    public Map<Path, List<DiscoveredMethod>> collectMethodsByFile(
411
            List<Path> roots, List<TestDiscovery> providers) throws IOException {
412
        Map<Path, List<DiscoveredMethod>> byFile = new LinkedHashMap<>();
413
        for (Path root : roots) {
414
            for (TestDiscovery provider : providers) {
415 1 1. collectMethodsByFile : removed call to java/util/stream/Stream::forEach → KILLED
                provider.discover(root).forEach(m -> {
416 2 1. lambda$collectMethodsByFile$1 : removed conditional - replaced equality check with true → SURVIVED
2. lambda$collectMethodsByFile$1 : removed conditional - replaced equality check with false → KILLED
                    if (m.filePath() != null) {
417 1 1. lambda$collectMethodsByFile$0 : replaced return value with Collections.emptyList for org/egothor/methodatlas/command/ScanOrchestrator::lambda$collectMethodsByFile$0 → KILLED
                        byFile.computeIfAbsent(m.filePath(), k -> new ArrayList<>()).add(m);
418
                    }
419
                });
420
            }
421
        }
422 1 1. collectMethodsByFile : replaced return value with Collections.emptyMap for org/egothor/methodatlas/command/ScanOrchestrator::collectMethodsByFile → KILLED
        return byFile;
423
    }
424
425
    /**
426
     * Resolves AI security-classification suggestions for every class in
427
     * {@code byClass} and populates {@code tagsToApply} and
428
     * {@code displayNames} with the results for methods that are
429
     * security-relevant.
430
     *
431
     * <p>
432
     * A display-name suggestion is only placed into {@code displayNames}
433
     * when the discovered method has no existing {@code @DisplayName} in
434
     * source (i.e. {@link DiscoveredMethod#displayName()} returns
435
     * {@code null}). This prevents AI-generated names from overwriting
436
     * manually authored ones.
437
     * </p>
438
     *
439
     * @param byClass      discovered methods grouped by FQCN for one source file
440
     * @param ai           AI runtime carrying the engine, override, and cache
441
     * @param aiCache      AI result cache used to compute the content-hash lookup key
442
     * @param tagsToApply  output accumulator: method name to tag values to write
443
     * @param displayNames output accumulator: method name to display name to write
444
     * @since 1.0.0
445
     */
446
    public void gatherAiSuggestionsForFile(Map<String, List<DiscoveredMethod>> byClass,
447
            AiRuntime ai, AiResultCache aiCache,
448
            Map<String, List<String>> tagsToApply, Map<String, String> displayNames) {
449 2 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → SURVIVED
        String promptSignature = ai.engine() == null ? null : ai.options().promptTemplates().signature();
450
        for (Map.Entry<String, List<DiscoveredMethod>> classEntry : byClass.entrySet()) {
451
            String fqcn = classEntry.getKey();
452
            List<DiscoveredMethod> classMethods = classEntry.getValue();
453
454
            // groupingBy never produces an empty value list; the first method is the
455
            // representative carrying the class-level fields (source content and file
456
            // stem are identical across a class's methods).
457
            DiscoveredMethod representative = classMethods.get(0);
458
            String classSource = representative.sourceContent().get().orElse(null);
459 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → NO_COVERAGE
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → NO_COVERAGE
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → SURVIVED
            String lookupHash = aiCache.isActive() && classSource != null
460
                    ? ContentHasher.hashClass(classSource) : null;
461
            String fileStem = representative.fileStem();
462
            List<String> methodNames = classMethods.stream().map(DiscoveredMethod::method).toList();
463
            List<PromptBuilder.TargetMethod> targetMethods = classMethods.stream()
464
                    .map(ScanOrchestrator::toTargetMethod).toList();
465
466
            SuggestionLookup suggestions = resolveSuggestionLookup(
467
                    fileStem, fqcn, classSource, methodNames, targetMethods, ai, lookupHash,
468
                    promptSignature, null).lookup();
469
470
            for (DiscoveredMethod m : classMethods) {
471
                AiMethodSuggestion suggestion = suggestions.find(m.method()).orElse(null);
472 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
                if (suggestion == null || !suggestion.securityRelevant()) {
473
                    continue;
474
                }
475 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
                if (suggestion.displayName() != null && !suggestion.displayName().isBlank()
476 2 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
                        && m.displayName() == null) {
477
                    displayNames.putIfAbsent(m.method(), suggestion.displayName());
478
                }
479 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
                if (suggestion.tags() != null && !suggestion.tags().isEmpty()) {
480
                    tagsToApply.putIfAbsent(m.method(), suggestion.tags());
481
                }
482
            }
483
        }
484
    }
485
486
    /**
487
     * Wraps a {@link TestMethodSink} so that only records that pass all
488
     * active filters are forwarded to {@code delegate}.
489
     *
490
     * <p>
491
     * Two independent filters are supported and composed in order:
492
     * </p>
493
     * <ol>
494
     *   <li><b>Security-only filter</b> — when {@code securityOnly} is
495
     *       {@code true}, records whose {@link AiMethodSuggestion} is
496
     *       {@code null} or has {@code securityRelevant=false} are dropped.</li>
497
     *   <li><b>Confidence threshold filter</b> — when {@code confidenceEnabled}
498
     *       is {@code true} <em>and</em> {@code minConfidence > 0.0}, records
499
     *       whose {@link AiMethodSuggestion} is {@code null} or has a
500
     *       {@link AiMethodSuggestion#confidence()} below {@code minConfidence}
501
     *       are dropped. This filter is a no-op when {@code confidenceEnabled}
502
     *       is {@code false} because the confidence field is always
503
     *       {@code 0.0} when confidence scoring was not requested.</li>
504
     * </ol>
505
     *
506
     * <p>
507
     * When neither filter is active the original {@code delegate} is
508
     * returned unchanged (zero overhead).
509
     * </p>
510
     *
511
     * @param delegate          the underlying sink to forward matching
512
     *                          records to
513
     * @param securityOnly      whether to enable the security-relevance filter
514
     * @param minConfidence     minimum confidence score (inclusive) required
515
     *                          to pass the confidence filter; {@code 0.0}
516
     *                          disables it
517
     * @param confidenceEnabled whether confidence scoring was requested;
518
     *                          must be {@code true} for the confidence
519
     *                          filter to activate
520
     * @return filtered sink, or {@code delegate} unchanged when all filters
521
     *         are off
522
     * @since 1.0.0
523
     */
524
    public TestMethodSink filterSink(TestMethodSink delegate, boolean securityOnly,
525
            double minConfidence, boolean confidenceEnabled) {
526
        TestMethodSink sink = delegate;
527 2 1. filterSink : removed conditional - replaced equality check with false → KILLED
2. filterSink : removed conditional - replaced equality check with true → KILLED
        if (securityOnly) {
528
            final TestMethodSink next = sink;
529
            sink = (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) -> {
530 4 1. lambda$filterSink$2 : removed conditional - replaced equality check with true → KILLED
2. lambda$filterSink$2 : removed conditional - replaced equality check with false → KILLED
3. lambda$filterSink$2 : removed conditional - replaced equality check with true → KILLED
4. lambda$filterSink$2 : removed conditional - replaced equality check with false → KILLED
                if (suggestion != null && suggestion.securityRelevant()) {
531 1 1. lambda$filterSink$2 : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
                    next.record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
532
                }
533
            };
534
        }
535 5 1. filterSink : removed conditional - replaced comparison check with true → SURVIVED
2. filterSink : changed conditional boundary → SURVIVED
3. filterSink : removed conditional - replaced equality check with true → KILLED
4. filterSink : removed conditional - replaced equality check with false → KILLED
5. filterSink : removed conditional - replaced comparison check with false → KILLED
        if (confidenceEnabled && minConfidence > 0.0) {
536
            final double threshold = minConfidence;
537
            final TestMethodSink next = sink;
538
            sink = (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) -> {
539 5 1. lambda$filterSink$3 : changed conditional boundary → SURVIVED
2. lambda$filterSink$3 : removed conditional - replaced comparison check with false → KILLED
3. lambda$filterSink$3 : removed conditional - replaced comparison check with true → KILLED
4. lambda$filterSink$3 : removed conditional - replaced equality check with true → KILLED
5. lambda$filterSink$3 : removed conditional - replaced equality check with false → KILLED
                if (suggestion != null && suggestion.confidence() >= threshold) {
540 1 1. lambda$filterSink$3 : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
                    next.record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
541
                }
542
            };
543
        }
544 1 1. filterSink : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::filterSink → KILLED
        return sink;
545
    }
546
547
    // -------------------------------------------------------------------------
548
    // Static utilities
549
    // -------------------------------------------------------------------------
550
551
    /**
552
     * Converts a single discovered test method into a prompt target descriptor.
553
     *
554
     * <p>
555
     * Exposed publicly because {@link ManualPrepareCommand} also needs to
556
     * build prompt-target lists from discovered methods when writing manual
557
     * work files; the conversion logic must stay aligned across both call
558
     * sites to keep the prompt format consistent.
559
     * </p>
560
     *
561
     * @param m discovered test method; must not be {@code null}
562
     * @return corresponding prompt target descriptor; never {@code null}
563
     * @see PromptBuilder.TargetMethod
564
     */
565
    public static PromptBuilder.TargetMethod toTargetMethod(DiscoveredMethod m) {
566 1 1. toTargetMethod : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::toTargetMethod → KILLED
        return new PromptBuilder.TargetMethod(
567
                m.method(),
568 3 1. toTargetMethod : changed conditional boundary → KILLED
2. toTargetMethod : removed conditional - replaced comparison check with true → KILLED
3. toTargetMethod : removed conditional - replaced comparison check with false → KILLED
                m.beginLine() > 0 ? m.beginLine() : null,
569 3 1. toTargetMethod : changed conditional boundary → KILLED
2. toTargetMethod : removed conditional - replaced comparison check with true → KILLED
3. toTargetMethod : removed conditional - replaced comparison check with false → KILLED
                m.endLine() > 0 ? m.endLine() : null);
570
    }
571
572
    // -------------------------------------------------------------------------
573
    // Private helpers
574
    // -------------------------------------------------------------------------
575
576
    /**
577
     * Resolves the AI answer for one class, consulting the signature-gated cache
578
     * before any provider call and serving cached credential verdicts when present.
579
     *
580
     * @param promptSignature the current run's prompt-catalogue signature, or
581
     *                        {@code null} when AI is disabled
582
     * @return the override-applied lookup plus the raw answer to cache (or
583
     *         {@code null} when nothing should be cached)
584
     */
585
    private static Resolved resolveSuggestionLookup(String fileStem, String fqcn,
586
            String classSource, List<String> methodNames, List<PromptBuilder.TargetMethod> targetMethods,
587
            AiRuntime ai, String contentHash, String promptSignature, CredentialTriageContext secretCtx) {
588 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → SURVIVED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
        if (methodNames.isEmpty()) {
589 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE
            return new Resolved(SuggestionLookup.from(null), null);
590
        }
591
592 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
        if (ai.engine() == null) {
593 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return new Resolved(SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames)), null);
594
        }
595
596
        List<PromptBuilder.CredentialCandidateRef> candidates =
597 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
                secretCtx == null ? List.of() : secretCtx.candidatesFor(fqcn);
598
599
        // Cache first, gated on the prompt-catalogue signature. A hit serves the
600
        // classification AND (when this run triages credentials) the verdicts —
601
        // from the same cached answer, with no provider call.
602
        AiClassSuggestion cached = ai.cache().classification(contentHash, promptSignature).orElse(null);
603 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
        if (cached != null) {
604 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
            AiClassSuggestion cacheable = candidates.isEmpty() ? cached
605
                    : serveOrTriageVerdicts(ai, fqcn, classSource, contentHash, promptSignature,
606
                            candidates, cached, secretCtx);
607 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return new Resolved(SuggestionLookup.from(ai.override().apply(fqcn, cached, methodNames)), cacheable);
608
        }
609
610 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → SURVIVED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
        if (classSource == null) {
611 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE
            return new Resolved(SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames)), null);
612
        }
613
614 5 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → SURVIVED
2. resolveSuggestionLookup : changed conditional boundary → SURVIVED
3. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
4. resolveSuggestionLookup : removed conditional - replaced comparison check with true → KILLED
5. resolveSuggestionLookup : removed conditional - replaced comparison check with false → KILLED
        if (ai.options().enabled() && classSource.length() > ai.options().maxClassChars()) {
615
            if (LOG.isLoggable(Level.INFO)) {
616
                LOG.log(Level.INFO, "Skipping AI for {0}: class source too large ({1} chars)",
617
                        new Object[] { fqcn, classSource.length() });
618
            }
619 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return new Resolved(SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames)), null);
620
        }
621
622
        if (LOG.isLoggable(Level.INFO)) {
623
            LOG.log(Level.INFO, "Querying AI for {0} ({1} methods)", new Object[] { fqcn, targetMethods.size() });
624
        }
625
626
        try {
627
            // Fold credential triage into the single classification call when there
628
            // are candidates, so the class source is sent to the provider once.
629
            AiClassSuggestion aiClassSuggestion;
630 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
            if (candidates.isEmpty()) {
631
                aiClassSuggestion = ai.engine().suggestForClass(fileStem, fqcn, classSource, targetMethods);
632
            } else {
633
                aiClassSuggestion =
634
                        ai.engine().suggestForClass(fileStem, fqcn, classSource, targetMethods, candidates);
635 1 1. resolveSuggestionLookup : removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED
                secretCtx.recordVerdicts(fqcn, aiClassSuggestion.secrets());
636
            }
637 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return new Resolved(
638
                    SuggestionLookup.from(ai.override().apply(fqcn, aiClassSuggestion, methodNames)),
639
                    aiClassSuggestion);
640
        } catch (AiSuggestionException e) {
641
            if (LOG.isLoggable(Level.WARNING)) {
642
                LOG.log(Level.WARNING, "AI suggestion failed for class " + fqcn, e);
643
            }
644 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return new Resolved(SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames)), null);
645
        }
646
    }
647
648
    /**
649
     * On a classification cache hit, supplies the credential verdicts: cached when
650
     * present for this signature, otherwise a one-off dedicated triage call (so a
651
     * class whose classification was cached without verdicts still gets scored).
652
     *
653
     * @return the cached suggestion augmented with the resolved verdicts, for re-caching
654
     */
655
    private static AiClassSuggestion serveOrTriageVerdicts(AiRuntime ai, String fqcn, String classSource,
656
            String contentHash, String promptSignature,
657
            List<PromptBuilder.CredentialCandidateRef> candidates, AiClassSuggestion cached,
658
            CredentialTriageContext secretCtx) {
659
        Optional<List<CredentialTriageVerdict>> cachedVerdicts =
660
                ai.cache().verdicts(contentHash, promptSignature);
661 2 1. serveOrTriageVerdicts : removed conditional - replaced equality check with false → KILLED
2. serveOrTriageVerdicts : removed conditional - replaced equality check with true → KILLED
        if (cachedVerdicts.isPresent()) {
662 1 1. serveOrTriageVerdicts : removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED
            secretCtx.recordVerdicts(fqcn, cachedVerdicts.get());
663 1 1. serveOrTriageVerdicts : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → SURVIVED
            return withSecrets(cached, cachedVerdicts.get());
664
        }
665 2 1. serveOrTriageVerdicts : removed conditional - replaced equality check with false → SURVIVED
2. serveOrTriageVerdicts : removed conditional - replaced equality check with true → KILLED
        if (classSource == null) {
666 1 1. serveOrTriageVerdicts : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → NO_COVERAGE
            return cached;
667
        }
668
        try {
669
            List<CredentialTriageVerdict> verdicts = ai.engine().triageSecrets(fqcn, classSource, candidates);
670 1 1. serveOrTriageVerdicts : removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED
            secretCtx.recordVerdicts(fqcn, verdicts);
671 1 1. serveOrTriageVerdicts : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → SURVIVED
            return withSecrets(cached, verdicts);
672
        } catch (AiSuggestionException e) {
673
            if (LOG.isLoggable(Level.WARNING)) {
674
                LOG.log(Level.WARNING, "Credential triage failed for cached class " + fqcn, e);
675
            }
676 1 1. serveOrTriageVerdicts : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → NO_COVERAGE
            return cached;
677
        }
678
    }
679
680
    /**
681
     * Returns a copy of {@code suggestion} with its credential verdicts replaced.
682
     *
683
     * @param suggestion the classification result to copy; never {@code null}
684
     * @param secrets    the verdicts to attach; may be {@code null}
685
     * @return a new suggestion carrying {@code secrets}
686
     */
687
    private static AiClassSuggestion withSecrets(AiClassSuggestion suggestion,
688
            List<CredentialTriageVerdict> secrets) {
689 1 1. withSecrets : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::withSecrets → SURVIVED
        return new AiClassSuggestion(suggestion.className(), suggestion.classSecurityRelevant(),
690
                suggestion.classTags(), suggestion.classReason(), suggestion.methods(), secrets);
691
    }
692
693
    /**
694
     * The outcome of resolving one class: the override-applied per-method lookup, and
695
     * the raw AI answer to persist in the cache ({@code null} when nothing should be
696
     * cached — no AI, no methods, oversized source, or a failed call).
697
     *
698
     * @param lookup    per-method suggestion lookup fed to the sink; never {@code null}
699
     * @param cacheable the full AI answer to cache, or {@code null}
700
     */
701
    private record Resolved(SuggestionLookup lookup, AiClassSuggestion cacheable) {
702
    }
703
704
    /**
705
     * The result of scanning one root: whether any provider errored, and the unified
706
     * cache entries collected for the classes processed in that root.
707
     *
708
     * @param hadErrors    {@code true} if any provider reported a non-fatal error
709
     * @param cacheEntries cache entries keyed by content hash; never {@code null}
710
     */
711
    private record DiscoveryResult(boolean hadErrors, Map<String, AiCacheEntry> cacheEntries) {
712
    }
713
}

Mutations

165

1.1
Location : scan
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/command/ScanOrchestrator::scan → SURVIVED
Covering tests

197

1.1
Location : scan
Killed by : org.egothor.methodatlas.GitHubAnnotationsEmitterTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.GitHubAnnotationsEmitterTest]/[method:app_githubAnnotationsMode_emptyDirectoryProducesNoOutput(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

202

1.1
Location : scan
Killed by : org.egothor.methodatlas.EvidencePackCommandTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.EvidencePackCommandTest]/[method:caseInsensitiveFrameworkTokenIsAccepted(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

205

1.1
Location : scan
Killed by : org.egothor.methodatlas.GitHubAnnotationsEmitterTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.GitHubAnnotationsEmitterTest]/[method:app_githubAnnotationsMode_emptyDirectoryProducesNoOutput(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

206

1.1
Location : scan
Killed by : none
removed call to java/util/Map::putAll → NO_COVERAGE

210

1.1
Location : scan
Killed by : none
removed call to org/egothor/methodatlas/command/PluginLoader::closeAll → SURVIVED
Covering tests

212

1.1
Location : scan
Killed by : org.egothor.methodatlas.GitHubAnnotationsEmitterTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.GitHubAnnotationsEmitterTest]/[method:app_githubAnnotationsMode_emptyDirectoryProducesNoOutput(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

213

1.1
Location : scan
Killed by : none
removed call to org/egothor/methodatlas/AiCacheStore::write → NO_COVERAGE

215

1.1
Location : scan
Killed by : org.egothor.methodatlas.EvidencePackCommandTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.EvidencePackCommandTest]/[method:caseInsensitiveFrameworkTokenIsAccepted(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

249

1.1
Location : runDiscovery
Killed by : none
replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED
Covering tests

2.2
Location : runDiscovery
Killed by : none
replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED Covering tests

276

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:emitCoverage_writesReportToCustomPath(java.nio.file.Path)]
replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED

2.2
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_unparseableFile_returnsExitCode1(java.nio.file.Path)]
replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED

298

1.1
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed call to java/util/stream/Stream::forEach → KILLED

299

1.1
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:emitCoverage_writesReportToCustomPath(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_unparseableFile_returnsExitCode1(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

311

1.1
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withoutVerdicts_issuesExactlyOneDedicatedTriageCall(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : runDiscoveryInternal
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

315

1.1
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed call to org/egothor/methodatlas/command/ScanOrchestrator::processClass → KILLED

319

1.1
Location : runDiscoveryInternal
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::runDiscoveryInternal → KILLED

349

1.1
Location : processClass
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

3.3
Location : processClass
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

4.4
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

5.5
Location : processClass
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

6.6
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

350

1.1
Location : processClass
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : processClass
Killed by : org.egothor.methodatlas.MethodAtlasAppContentHashTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppContentHashTest]/[method:plainMode_hashTokenPresent_whenEnabled(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

352

1.1
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : processClass
Killed by : org.egothor.methodatlas.MethodAtlasAppContentHashTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppContentHashTest]/[method:plainMode_hashTokenPresent_whenEnabled(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

365

1.1
Location : processClass
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

370

1.1
Location : processClass
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

3.3
Location : processClass
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

4.4
Location : processClass
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

384

1.1
Location : wrapWithExtraSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : wrapWithExtraSink
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:accessControlAnnotation_mapsToAsvs4Dot1Dot1(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

385

1.1
Location : wrapWithExtraSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::wrapWithExtraSink → KILLED

387

1.1
Location : wrapWithExtraSink
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:coveragePercent_isInClosedZeroToHundredRange(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::wrapWithExtraSink → KILLED

415

1.1
Location : collectMethodsByFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed call to java/util/stream/Stream::forEach → KILLED

416

1.1
Location : lambda$collectMethodsByFile$1
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : lambda$collectMethodsByFile$1
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

417

1.1
Location : lambda$collectMethodsByFile$0
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
replaced return value with Collections.emptyList for org/egothor/methodatlas/command/ScanOrchestrator::lambda$collectMethodsByFile$0 → KILLED

422

1.1
Location : collectMethodsByFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
replaced return value with Collections.emptyMap for org/egothor/methodatlas/command/ScanOrchestrator::collectMethodsByFile → KILLED

449

1.1
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

459

1.1
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

2.2
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

3.3
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

4.4
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

472

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_nonSecurityMethod_notAnnotated(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

3.3
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

4.4
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

475

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsDisplayNameImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

3.3
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

4.4
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsDisplayNameImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

476

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsDisplayNameImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_existingAnnotationsNotDuplicated(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

479

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

3.3
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

4.4
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

527

1.1
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_neitherFilterActive_returnsDelegateUnchanged()]
removed conditional - replaced equality check with true → KILLED

530

1.1
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with true → KILLED

2.2
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with true → KILLED

4.4
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

531

1.1
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

535

1.1
Location : filterSink
Killed by : org.egothor.methodatlas.MethodAtlasAppJsonTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppJsonTest]/[method:minConfidence_noEffectWithoutAiConfidence(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : filterSink
Killed by : none
removed conditional - replaced comparison check with true → SURVIVED
Covering tests

4.4
Location : filterSink
Killed by : none
changed conditional boundary → SURVIVED Covering tests

5.5
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with false → KILLED

539

1.1
Location : lambda$filterSink$3
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with false → KILLED

3.3
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with true → KILLED

4.4
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with true → KILLED

5.5
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with false → KILLED

540

1.1
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

544

1.1
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_neitherFilterActive_returnsDelegateUnchanged()]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::filterSink → KILLED

566

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::toTargetMethod → KILLED

568

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
changed conditional boundary → KILLED

2.2
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
removed conditional - replaced comparison check with true → KILLED

3.3
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_validBeginAndEndLines_preservesBoth()]
removed conditional - replaced comparison check with false → KILLED

569

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
changed conditional boundary → KILLED

2.2
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
removed conditional - replaced comparison check with true → KILLED

3.3
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_validBeginAndEndLines_preservesBoth()]
removed conditional - replaced comparison check with false → KILLED

588

1.1
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

589

1.1
Location : resolveSuggestionLookup
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE

592

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

593

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

597

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_noImportsAddedWhenNothingAnnotated(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

603

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

604

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:aiCache_hit_engineNotCalledForUnchangedClass(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

607

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

610

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

611

1.1
Location : resolveSuggestionLookup
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE

614

1.1
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

3.3
Location : resolveSuggestionLookup
Killed by : none
changed conditional boundary → SURVIVED Covering tests

4.4
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced comparison check with true → KILLED

5.5
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
removed conditional - replaced comparison check with false → KILLED

619

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

630

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_noImportsAddedWhenNothingAnnotated(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

635

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED

637

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheMiss_runsCombinedClassificationAndRecordsVerdicts(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

644

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:plainMode_aiFailureForOneClass_continuesScanningAndFallsBackForThatClass(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

661

1.1
Location : serveOrTriageVerdicts
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : serveOrTriageVerdicts
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withoutVerdicts_issuesExactlyOneDedicatedTriageCall(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

662

1.1
Location : serveOrTriageVerdicts
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withMatchingSignatureAndVerdicts_servesBothWithoutAnyEngineCall(java.nio.file.Path)]
removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED

663

1.1
Location : serveOrTriageVerdicts
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → SURVIVED
Covering tests

665

1.1
Location : serveOrTriageVerdicts
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : serveOrTriageVerdicts
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withoutVerdicts_issuesExactlyOneDedicatedTriageCall(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

666

1.1
Location : serveOrTriageVerdicts
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → NO_COVERAGE

670

1.1
Location : serveOrTriageVerdicts
Killed by : org.egothor.methodatlas.command.ScanOrchestratorCacheTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorCacheTest]/[method:cacheHit_withoutVerdicts_issuesExactlyOneDedicatedTriageCall(java.nio.file.Path)]
removed call to org/egothor/methodatlas/command/CredentialTriageContext::recordVerdicts → KILLED

671

1.1
Location : serveOrTriageVerdicts
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → SURVIVED
Covering tests

676

1.1
Location : serveOrTriageVerdicts
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::serveOrTriageVerdicts → NO_COVERAGE

689

1.1
Location : withSecrets
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::withSecrets → SURVIVED
Covering tests

Active mutators

Tests examined


Report generated by PIT 1.22.1