MethodAtlasApp.java

1
package org.egothor.methodatlas;
2
3
import java.io.FilterOutputStream;
4
import java.io.IOException;
5
import java.io.OutputStream;
6
import java.io.OutputStreamWriter;
7
import java.io.PrintWriter;
8
import java.nio.charset.StandardCharsets;
9
import java.nio.file.Path;
10
import java.util.logging.Level;
11
import java.util.logging.Logger;
12
13
import org.egothor.methodatlas.ai.ManualConsumeEngine;
14
import org.egothor.methodatlas.ai.AiSuggestionEngine;
15
import org.egothor.methodatlas.api.TestDiscoveryConfig;
16
import org.egothor.methodatlas.command.AiRuntimeBuilder;
17
import org.egothor.methodatlas.command.ApplyTagsCommand;
18
19
import org.egothor.methodatlas.emit.ClassificationOverride;
20
import org.egothor.methodatlas.emit.OutputMode;
21
import org.egothor.methodatlas.command.ApplyTagsFromCsvCommand;
22
import org.egothor.methodatlas.command.CheckPromptsCommand;
23
import org.egothor.methodatlas.command.CommentRedaction;
24
import org.egothor.methodatlas.command.DiffCommand;
25
import org.egothor.methodatlas.command.EvalAccuracyCommand;
26
import org.egothor.methodatlas.command.GitHubAnnotationsCommand;
27
import org.egothor.methodatlas.command.JsonCommand;
28
import org.egothor.methodatlas.command.ManualPrepareCommand;
29
import org.egothor.methodatlas.command.OverrideLoader;
30
import org.egothor.methodatlas.command.PluginLoader;
31
import org.egothor.methodatlas.command.SarifCommand;
32
import org.egothor.methodatlas.command.ScanCommand;
33
import org.egothor.methodatlas.command.ScanOrchestrator;
34
import org.egothor.methodatlas.command.RedactionAbortedException;
35
import org.egothor.methodatlas.coverage.CoverageFacade;
36
import org.egothor.methodatlas.attest.AttestFacade;
37
import org.egothor.methodatlas.evidencereport.EvidenceReportFacade;
38
import org.egothor.methodatlas.gap.GapFacade;
39
import org.egothor.methodatlas.receipt.ReceiptFacade;
40
import org.egothor.methodatlas.evidence.EvidenceFramework;
41
import org.egothor.methodatlas.evidence.EvidencePackCommand;
42
import org.egothor.methodatlas.evidence.EvidencePackOptions;
43
import org.egothor.methodatlas.evidence.ExportKeyringSecretCommand;
44
import org.egothor.methodatlas.evidence.GenSigningKeyCommand;
45
46
/**
47
 * Command-line entry point that parses the arguments and routes the invocation
48
 * to the matching {@link org.egothor.methodatlas.command.Command} or utility
49
 * mode.
50
 *
51
 * <p>
52
 * This class owns no discovery or parsing logic of its own. It parses arguments
53
 * into a {@link CliConfig} (via {@link CliArgs}), selects the operating mode,
54
 * builds the collaborators that mode needs, and delegates execution. Test
55
 * discovery is performed by the language plugins on the classpath (loaded
56
 * through {@link java.util.ServiceLoader}); AI enrichment, output emission, and
57
 * the evidence-pack, coverage, and receipt features are each handled by their
58
 * own command or facade.
59
 * </p>
60
 *
61
 * <h2>Source-Derived Metadata</h2>
62
 *
63
 * <p>
64
 * For each discovered test method, the application reports:
65
 * </p>
66
 * <ul>
67
 * <li>fully qualified class name</li>
68
 * <li>method name</li>
69
 * <li>inclusive line count of the method declaration</li>
70
 * <li>JUnit {@code @Tag} values declared on the method</li>
71
 * </ul>
72
 *
73
 * <h2>AI Enrichment</h2>
74
 *
75
 * <p>
76
 * When AI support is enabled, the application submits each discovered test
77
 * class to an {@link org.egothor.methodatlas.ai.AiSuggestionEngine} and merges
78
 * the returned method-level suggestions into the emitted output.
79
 * </p>
80
 *
81
 * <h2>Manual AI Workflow</h2>
82
 *
83
 * <p>
84
 * Operators who cannot access an AI API directly can use the two-phase manual
85
 * workflow:
86
 * </p>
87
 * <ol>
88
 * <li><b>Prepare phase</b> ({@code -manual-prepare}): the application scans
89
 * test sources and writes one work file per class to the specified directory.
90
 * Each work file contains operator instructions and the full AI prompt (with
91
 * class source embedded). No CSV output is produced in this phase.</li>
92
 * <li><b>Consume phase</b> ({@code -manual-consume}): the application reads
93
 * operator-saved AI response files ({@code <stem>.response.txt}) from the
94
 * response directory and produces the final enriched CSV. Classes whose
95
 * response file is absent receive empty AI columns.</li>
96
 * </ol>
97
 *
98
 * <h2>Supported Command-Line Options</h2>
99
 *
100
 * <ul>
101
 * <li>{@code -config <path>} — loads default values from a YAML configuration
102
 * file; command-line flags override YAML values</li>
103
 * <li>{@code -plain} — emits plain text output instead of CSV</li>
104
 * <li>{@code -sarif} — emits SARIF 2.1.0 JSON output</li>
105
 * <li>{@code -json} — emits a flat JSON array; each element carries the same
106
 * fields as CSV with {@code tags} and {@code ai_tags} as arrays and numeric
107
 * values as JSON numbers</li>
108
 * <li>{@code -ai} — enables AI-based enrichment</li>
109
 * <li>{@code -ai-provider <provider>} — selects the AI provider</li>
110
 * <li>{@code -ai-model <model>} — selects the provider-specific model</li>
111
 * <li>{@code -ai-base-url <url>} — overrides the provider base URL</li>
112
 * <li>{@code -ai-api-key <key>} — supplies the AI API key directly</li>
113
 * <li>{@code -ai-api-key-env <name>} — resolves the AI API key from an
114
 * environment variable</li>
115
 * <li>{@code -ai-taxonomy <path>} — loads taxonomy text from an external
116
 * file</li>
117
 * <li>{@code -ai-taxonomy-mode <mode>} — selects the built-in taxonomy
118
 * variant</li>
119
 * <li>{@code -ai-max-class-chars <count>} — limits class source size submitted
120
 * to AI</li>
121
 * <li>{@code -ai-timeout-sec <seconds>} — sets the AI request timeout</li>
122
 * <li>{@code -ai-max-retries <count>} — sets the retry limit for AI
123
 * operations</li>
124
 * <li>{@code -ai-confidence} — requests a confidence score for each AI
125
 * security classification; adds an {@code ai_confidence} column to the
126
 * output</li>
127
 * <li>{@code -min-confidence <threshold>} — silently drops methods whose AI
128
 * confidence score is below {@code threshold} (range {@code 0.0–1.0}); only
129
 * effective when {@code -ai-confidence} is also enabled</li>
130
 * <li>{@code -ai-cache <path>} — loads a previous scan CSV produced with
131
 * {@code -content-hash -ai} as an AI result cache; classes whose
132
 * {@code content_hash} matches a cache entry are classified without an API
133
 * call; changed and new classes are classified normally</li>
134
 * <li>{@code -file-suffix <suffix>} — matches source files by name suffix
135
 * (default: {@code Test.java}); may be repeated to match multiple patterns,
136
 * e.g. {@code -file-suffix Test.java -file-suffix IT.java}; the first
137
 * occurrence replaces the default</li>
138
 * <li>{@code -test-annotation <name>} — recognises methods annotated with
139
 * {@code name} as test methods; may be repeated; the first occurrence replaces
140
 * the JVM provider's default set (JUnit 5 {@code Test}, {@code ParameterizedTest},
141
 * {@code RepeatedTest}, {@code TestFactory}, {@code TestTemplate})</li>
142
 * <li>{@code -emit-metadata} — emits {@code # key: value} comment lines
143
 * before the header row describing the tool version, scan timestamp, and
144
 * taxonomy configuration</li>
145
 * <li>{@code -apply-tags} — instead of emitting a report, writes
146
 * AI-generated {@code @DisplayName} and {@code @Tag} annotations back to
147
 * the scanned source files; requires AI enrichment to be enabled</li>
148
 * <li>{@code -content-hash} — includes a SHA-256 fingerprint of each class
149
 * source as a {@code content_hash} column in CSV/plain output and as a SARIF
150
 * property; useful for detecting which classes changed between scans</li>
151
 * <li>{@code -security-only} — suppresses non-security methods from the output;
152
 * only methods whose AI classification (or override) has
153
 * {@code securityRelevant=true} are emitted; requires AI enrichment or a
154
 * classification override file to have any effect</li>
155
 * <li>{@code -manual-prepare <workdir> <responsedir>} — runs the manual AI
156
 * prepare phase, writing work files to {@code workdir} and empty response stubs
157
 * to {@code responsedir}; the two paths may be identical</li>
158
 * <li>{@code -manual-consume <workdir> <responsedir>} — runs the manual AI
159
 * consume phase, reading response files from {@code responsedir} and emitting
160
 * the final enriched CSV</li>
161
 * <li>{@code -diff <before.csv> <after.csv>} — compares two MethodAtlas scan
162
 * outputs and emits a delta report showing added, removed, and modified test
163
 * methods; all other flags are ignored when {@code -diff} is present</li>
164
 * <li>{@code -apply-tags-from-csv <path>} — instead of emitting a report,
165
 * applies the annotation decisions recorded in the reviewed CSV back to the
166
 * source files; the CSV is treated as a complete desired-state specification:
167
 * every test method's {@code @Tag} set and {@code @DisplayName} are driven
168
 * entirely by the corresponding CSV row</li>
169
 * <li>{@code -mismatch-limit <n>} — when used with {@code -apply-tags-from-csv},
170
 * aborts without modifying any source file if the number of mismatches between
171
 * the CSV and the current source tree reaches or exceeds {@code n}; {@code -1}
172
 * (the default) logs mismatches as warnings and proceeds</li>
173
 * <li>{@code -promote-ai} — <strong>risky, not recommended</strong> opt-in for
174
 * {@code -apply-tags-from-csv}: fills blank tags/display_name from the AI columns
175
 * (unvalidated AI output reaches source); off by default</li>
176
 * <li>{@code -help} / {@code --help} / {@code -h} — print usage and exit</li>
177
 * <li>{@code -emit-source-root} — adds a {@code source_root} column (CSV) and a
178
 * {@code SRCROOT=} token (plain text) identifying which scan root each record
179
 * came from; essential in multi-root projects where the same fully qualified
180
 * class name can appear under different source trees; no effect on SARIF or
181
 * GitHub Annotations output</li>
182
 * </ul>
183
 *
184
 * <p>
185
 * Any remaining non-option arguments are interpreted as root paths to scan. If
186
 * no scan path is supplied, the current working directory is scanned.
187
 * </p>
188
 *
189
 * @see org.egothor.methodatlas.ai.AiSuggestionEngine
190
 * @see org.egothor.methodatlas.api.SourcePatcher
191
 * @see org.egothor.methodatlas.emit.OutputEmitter
192
 * @see org.egothor.methodatlas.emit.SarifEmitter
193
 * @see org.egothor.methodatlas.command.Command
194
 * @see #main(String[])
195
 */
196
@SuppressWarnings("PMD.CyclomaticComplexity")
197
public final class MethodAtlasApp {
198
199
    private static final String FLAG_DIFF = "-diff";
200
    private static final String FLAG_REQUIRE_JUSTIFICATION = "-require-justification";
201
    private static final String FLAG_DIFF_OVERRIDE_FILE = "-override-file";
202
    private static final int SINGLE_SINK = 1;
203
204
    /** Logger for receipt-emission warnings; receipt failures never abort the scan. */
205
    private static final Logger LOG = Logger.getLogger(MethodAtlasApp.class.getName());
206
207
    /** Tool version fallback when the JAR manifest carries no Implementation-Version. */
208
    private static final String DEV_VERSION = "dev";
209
210
    /** Exit code returned when CLI validation rejects the requested invocation. */
211
    private static final int EXIT_BAD_ARGS = 2;
212
    /** Exit code used when an evidence-pack framework token cannot be parsed. */
213
    private static final int EXIT_BAD_FRAMEWORK = 2;
214
    /** Exit code returned when the run fails with an expected, reported error. */
215
    private static final int EXIT_RUN_ERROR = 1;
216
217
    /**
218
     * Prevents instantiation of this utility class.
219
     */
220
    private MethodAtlasApp() {
221
    }
222
223
    /**
224
     * Program entry point.
225
     *
226
     * <p>
227
     * Delegates all work to {@link #run(String[], PrintWriter)}. The expected
228
     * failure modes — an I/O error traversing a scan tree, a rejected option, or
229
     * an AI engine that cannot be built — are caught here, reported as a single
230
     * concise line on {@code System.err}, and turned into exit code
231
     * {@value #EXIT_RUN_ERROR}; the raw stack trace is not shown. Unexpected
232
     * runtime failures are deliberately left to propagate so genuine defects
233
     * still surface with a full trace. A non-zero status is also returned when a
234
     * source file could not be processed.
235
     * </p>
236
     *
237
     * @param args command-line arguments
238
     */
239
    public static void main(String[] args) {
240
        // Wrap System.out in a guarded stream whose close() only flushes.
241
        // This lets try-with-resources manage the PrintWriter (satisfying
242
        // SpotBugs CloseResource and PMD UseTryWithResources) without
243
        // permanently closing System.out (satisfying Error Prone's
244
        // ClosingStandardOutputStreams check).
245
        OutputStream guarded = new FilterOutputStream(System.out) {
246
            @Override
247
            public void close() throws IOException {
248
                flush(); // flush but do NOT close System.out
249
            }
250
        };
251
        int exitCode;
252
        try (PrintWriter out = new PrintWriter(new OutputStreamWriter(guarded, StandardCharsets.UTF_8), true)) {
253
            exitCode = run(args, out);
254
        } catch (IOException | IllegalArgumentException | IllegalStateException e) {
255
            // Expected, actionable failures: report one concise line, no stack
256
            // trace. Unexpected RuntimeExceptions are not caught here so real
257
            // bugs still surface with a full trace.
258 2 1. main : removed conditional - replaced equality check with false → NO_COVERAGE
2. main : removed conditional - replaced equality check with true → NO_COVERAGE
            String detail = e.getMessage() != null ? e.getMessage() : e.toString();
259
            System.err.println("methodatlas: " + detail);
260
            exitCode = EXIT_RUN_ERROR;
261
        }
262 2 1. main : removed conditional - replaced equality check with false → NO_COVERAGE
2. main : removed conditional - replaced equality check with true → NO_COVERAGE
        if (exitCode != 0) {
263
            System.exit(exitCode);
264
        }
265
    }
266
267
    /**
268
     * Executes a full application run by routing to the appropriate
269
     * {@link org.egothor.methodatlas.command.Command} implementation.
270
     *
271
     * <p>
272
     * This method is the primary entry point for programmatic and test use. It
273
     * parses arguments, identifies the requested operating mode, constructs the
274
     * matching command object, and delegates execution to it.
275
     * </p>
276
     *
277
     * @param args command-line arguments
278
     * @param out  writer that receives all emitted output
279
     * @return {@code 0} if all files were processed successfully, {@code 1} if
280
     *         any file produced a parse or processing error
281
     * @throws IOException              if traversal of a configured file tree fails
282
     * @throws IllegalArgumentException if an option is unknown, if a required
283
     *                                  option value is missing, or if an option
284
     *                                  value cannot be parsed
285
     * @throws IllegalStateException    if AI support is enabled but the AI engine
286
     *                                  cannot be created successfully
287
     */
288
    /* default */ static int run(String[] args, PrintWriter out) throws IOException {
289
        // -help is handled before argument parsing so it works even with no
290
        // other (or otherwise invalid) arguments and never trips the
291
        // "Unknown argument" path.
292
        for (String arg : args) {
293 6 1. run : removed conditional - replaced equality check with false → KILLED
2. run : removed conditional - replaced equality check with false → KILLED
3. run : removed conditional - replaced equality check with true → KILLED
4. run : removed conditional - replaced equality check with true → KILLED
5. run : removed conditional - replaced equality check with false → KILLED
6. run : removed conditional - replaced equality check with true → KILLED
            if ("-help".equals(arg) || "--help".equals(arg) || "-h".equals(arg)) {
294
                Usage.print(out);
295
                return 0;
296
            }
297
        }
298
299
        // -diff, -gen-signing-key and -export-keyring-secret are utility modes handled before full
300
        // argument parsing; each owns its own small set of flags and never
301
        // participates in the scan pipeline, so all other flags are ignored.
302 3 1. run : removed conditional - replaced comparison check with false → KILLED
2. run : removed conditional - replaced comparison check with true → KILLED
3. run : changed conditional boundary → KILLED
        for (int i = 0; i < args.length; i++) {
303 2 1. run : removed conditional - replaced equality check with false → KILLED
2. run : removed conditional - replaced equality check with true → KILLED
            if (FLAG_DIFF.equals(args[i])) {
304 4 1. run : changed conditional boundary → SURVIVED
2. run : removed conditional - replaced comparison check with false → SURVIVED
3. run : Replaced integer addition with subtraction → SURVIVED
4. run : removed conditional - replaced comparison check with true → KILLED
                if (i + 2 >= args.length) {
305
                    throw new IllegalArgumentException(
306
                            "-diff requires two arguments: -diff <before.csv> <after.csv>");
307
                }
308 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → SURVIVED
                return buildDiffCommand(args, i).execute(out);
309
            }
310 2 1. run : removed conditional - replaced equality check with false → SURVIVED
2. run : removed conditional - replaced equality check with true → KILLED
            if (GenSigningKeyCommand.FLAG_GEN_SIGNING_KEY.equals(args[i])) {
311 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE
                return GenSigningKeyCommand.run(args, out);
312
            }
313 2 1. run : removed conditional - replaced equality check with false → SURVIVED
2. run : removed conditional - replaced equality check with true → KILLED
            if (ExportKeyringSecretCommand.FLAG_EXPORT_KEYRING_SECRET.equals(args[i])) {
314 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE
                return ExportKeyringSecretCommand.run(args, out);
315
            }
316 2 1. run : removed conditional - replaced equality check with false → SURVIVED
2. run : removed conditional - replaced equality check with true → KILLED
            if (EvalAccuracyCommand.FLAG_EVAL_ACCURACY.equals(args[i])) {
317 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE
                return EvalAccuracyCommand.fromArgs(args).execute(out);
318
            }
319 2 1. run : removed conditional - replaced equality check with false → SURVIVED
2. run : removed conditional - replaced equality check with true → KILLED
            if (CheckPromptsCommand.FLAG_CHECK_PROMPTS.equals(args[i])) {
320 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE
                return CheckPromptsCommand.fromArgs(args).execute(out);
321
            }
322
        }
323
324
        CliConfig cliConfig = CliArgs.parse(args);
325 2 1. run : removed conditional - replaced equality check with false → KILLED
2. run : removed conditional - replaced equality check with true → KILLED
        if (cliConfig == null) {
326
            // CliArgs already printed an actionable stderr message; signal
327
            // bad-args via the conventional exit code 2.
328 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED
            return EXIT_BAD_ARGS;
329
        }
330
331
        // Load the coverage mapping up-front so a malformed file aborts the run
332
        // before the scan starts, avoiding wasted work and ambiguous errors.
333
        CoverageFacade.Handle coverageHandle;
334
        try {
335
            coverageHandle = prepareCoverageHandle(cliConfig);
336
        } catch (IOException | IllegalArgumentException e) {
337
            System.err.println("Error loading coverage mapping: " + e.getMessage());
338 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED
            return EXIT_BAD_ARGS;
339
        }
340
341
        GapFacade.Handle gapHandle = prepareGapHandle(cliConfig);
342
        EvidenceReportFacade.Handle evidenceHandle = prepareEvidenceReportHandle(cliConfig);
343
        AttestFacade.Handle attestHandle = prepareAttestHandle(cliConfig);
344
345
        // Establish the run identity once and place it in the thread-local
346
        // context so the JUL formatter (Item 20) can prepend the correlation
347
        // id to every log record emitted during this invocation. clear() in
348
        // the finally block keeps the thread-local from outliving the run
349
        // when MethodAtlas is invoked programmatically (the standard CLI
350
        // exits the JVM anyway).
351
        String version = MethodAtlasApp.class.getPackage().getImplementationVersion();
352
        ScanRun scanRun = ScanRun.create(version, cliConfig.toString());
353
        ScanRunContext.set(scanRun);
354
        try {
355
            int exit = runWithScanRun(out, cliConfig, coverageHandle, gapHandle, evidenceHandle,
356
                    attestHandle);
357
            writeSideCarReports(cliConfig, version, coverageHandle, gapHandle, evidenceHandle,
358
                    attestHandle);
359 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED
            return exit;
360
        } catch (RedactionAbortedException e) {
361
            // Fail-closed by design: a clear one-line reason (file and language), no stack
362
            // trace, and no side-car reports for an aborted run.
363
            System.err.println("methodatlas: " + e.getMessage());
364 1 1. run : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED
            return EXIT_RUN_ERROR;
365
        } finally {
366
            ScanRunContext.clear();
367
        }
368
    }
369
370
    /**
371
     * Writes all side-car reports that were requested for this run.
372
     * Extracted from {@link #run} to keep that method's cyclomatic complexity in check.
373
     *
374
     * @param cliConfig      parsed CLI configuration
375
     * @param version        resolved tool version string
376
     * @param coverageHandle coverage handle; {@code null} when coverage is not active
377
     * @param gapHandle      gap-report handle; {@code null} when gap report is not active
378
     * @param evidenceHandle evidence-report handle; {@code null} when not active
379
     * @param attestHandle   attestation handle; {@code null} when not active
380
     */
381
    private static void writeSideCarReports(CliConfig cliConfig, String version,
382
            CoverageFacade.Handle coverageHandle, GapFacade.Handle gapHandle,
383
            EvidenceReportFacade.Handle evidenceHandle, AttestFacade.Handle attestHandle) {
384 2 1. writeSideCarReports : removed conditional - replaced equality check with false → KILLED
2. writeSideCarReports : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.emitReceipt()) {
385
            emitReceipt(cliConfig, version);
386
        }
387 2 1. writeSideCarReports : removed conditional - replaced equality check with false → KILLED
2. writeSideCarReports : removed conditional - replaced equality check with true → KILLED
        if (coverageHandle != null) {
388
            writeCoverage(cliConfig, version, coverageHandle);
389
        }
390 2 1. writeSideCarReports : removed conditional - replaced equality check with false → SURVIVED
2. writeSideCarReports : removed conditional - replaced equality check with true → KILLED
        if (gapHandle != null) {
391
            writeGapReport(cliConfig, version, gapHandle);
392
        }
393 2 1. writeSideCarReports : removed conditional - replaced equality check with false → SURVIVED
2. writeSideCarReports : removed conditional - replaced equality check with true → KILLED
        if (evidenceHandle != null) {
394
            writeEvidenceReport(cliConfig, version, evidenceHandle);
395
        }
396 2 1. writeSideCarReports : removed conditional - replaced equality check with false → SURVIVED
2. writeSideCarReports : removed conditional - replaced equality check with true → KILLED
        if (attestHandle != null) {
397
            writeAttestation(cliConfig, version, attestHandle);
398
        }
399
    }
400
401
    /**
402
     * Constructs a {@link DiffCommand} from raw args, scanning for
403
     * {@code -require-justification} and {@code -override-file} beyond the two
404
     * positional CSV paths.
405
     *
406
     * @param args  raw argument array; {@code args[diffIdx]} must equal {@code -diff}
407
     * @param diffIdx index of the {@code -diff} flag in {@code args}
408
     * @return configured command
409
     */
410
    private static DiffCommand buildDiffCommand(String[] args, int diffIdx) {
411 1 1. buildDiffCommand : Replaced integer addition with subtraction → KILLED
        Path diffBefore = Path.of(args[diffIdx + 1]);
412 1 1. buildDiffCommand : Replaced integer addition with subtraction → KILLED
        Path diffAfter = Path.of(args[diffIdx + 2]);
413
        boolean requireJustification = false;
414
        Path diffOverrideFile = null;
415 3 1. buildDiffCommand : removed conditional - replaced comparison check with false → SURVIVED
2. buildDiffCommand : removed conditional - replaced comparison check with true → KILLED
3. buildDiffCommand : changed conditional boundary → KILLED
        for (int j = 0; j < args.length; j++) {
416 2 1. buildDiffCommand : removed conditional - replaced equality check with true → SURVIVED
2. buildDiffCommand : removed conditional - replaced equality check with false → SURVIVED
            if (FLAG_REQUIRE_JUSTIFICATION.equals(args[j])) {
417
                requireJustification = true;
418 6 1. buildDiffCommand : removed conditional - replaced comparison check with true → NO_COVERAGE
2. buildDiffCommand : changed conditional boundary → NO_COVERAGE
3. buildDiffCommand : removed conditional - replaced comparison check with false → NO_COVERAGE
4. buildDiffCommand : removed conditional - replaced equality check with false → SURVIVED
5. buildDiffCommand : Replaced integer addition with subtraction → NO_COVERAGE
6. buildDiffCommand : removed conditional - replaced equality check with true → SURVIVED
            } else if (FLAG_DIFF_OVERRIDE_FILE.equals(args[j]) && j + 1 < args.length) {
419 1 1. buildDiffCommand : Replaced integer addition with subtraction → NO_COVERAGE
                diffOverrideFile = Path.of(args[j + 1]);
420
            }
421
        }
422 1 1. buildDiffCommand : replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::buildDiffCommand → KILLED
        return new DiffCommand(diffBefore, diffAfter, requireJustification, diffOverrideFile);
423
    }
424
425
    /**
426
     * Loads the coverage mapping when {@code -emit-coverage} is active.
427
     *
428
     * @param cliConfig parsed CLI configuration
429
     * @return prepared handle or {@code null} when coverage mode is not active
430
     * @throws IOException              if the mapping file cannot be read
431
     * @throws IllegalArgumentException if the mapping file fails validation
432
     */
433
    private static CoverageFacade.Handle prepareCoverageHandle(CliConfig cliConfig)
434
            throws IOException {
435 2 1. prepareCoverageHandle : removed conditional - replaced equality check with false → KILLED
2. prepareCoverageHandle : removed conditional - replaced equality check with true → KILLED
        if (!cliConfig.emitCoverage()) {
436
            return null;
437
        }
438 1 1. prepareCoverageHandle : replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareCoverageHandle → KILLED
        return CoverageFacade.prepare(cliConfig.coverageMappingFile(), cliConfig.minConfidence());
439
    }
440
441
    /**
442
     * Writes the coverage report. Errors are logged and swallowed so a
443
     * coverage-write failure never demotes a successful scan.
444
     *
445
     * @param cliConfig parsed CLI configuration
446
     * @param version   resolved tool version
447
     * @param handle    prepared coverage handle; never {@code null}
448
     */
449
    private static void writeCoverage(CliConfig cliConfig, String version,
450
            CoverageFacade.Handle handle) {
451 2 1. writeCoverage : removed conditional - replaced equality check with true → SURVIVED
2. writeCoverage : removed conditional - replaced equality check with false → SURVIVED
        String toolVersion = version != null ? version : DEV_VERSION;
452 2 1. writeCoverage : removed conditional - replaced equality check with true → SURVIVED
2. writeCoverage : removed conditional - replaced equality check with false → KILLED
        Path target = cliConfig.coverageFile() != null
453
                ? cliConfig.coverageFile()
454
                : Path.of(CoverageFacade.DEFAULT_COVERAGE_FILENAME);
455
        try {
456
            handle.write(toolVersion, target);
457
        } catch (IOException e) {
458
            if (LOG.isLoggable(Level.WARNING)) {
459
                LOG.log(Level.WARNING,
460
                        "Could not write coverage report: {0}", e.getMessage());
461
            }
462
        }
463
    }
464
465
    /**
466
     * Prepares a gap-report handle when {@code -gap-report} is active.
467
     *
468
     * @param cliConfig parsed CLI configuration
469
     * @return prepared handle, or {@code null} when gap-report mode is not active
470
     */
471
    private static GapFacade.Handle prepareGapHandle(CliConfig cliConfig) {
472 2 1. prepareGapHandle : removed conditional - replaced equality check with false → SURVIVED
2. prepareGapHandle : removed conditional - replaced equality check with true → SURVIVED
        if (!cliConfig.gapReport()) {
473
            return null;
474
        }
475 1 1. prepareGapHandle : replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareGapHandle → NO_COVERAGE
        return GapFacade.prepare(cliConfig.minConfidence());
476
    }
477
478
    /**
479
     * Writes the security-domain gap report. Errors are logged and swallowed so
480
     * a gap-report write failure never demotes a successful scan.
481
     *
482
     * @param cliConfig parsed CLI configuration
483
     * @param version   resolved tool version
484
     * @param handle    prepared gap-report handle; never {@code null}
485
     */
486
    private static void writeGapReport(CliConfig cliConfig, String version,
487
            GapFacade.Handle handle) {
488 2 1. writeGapReport : removed conditional - replaced equality check with true → NO_COVERAGE
2. writeGapReport : removed conditional - replaced equality check with false → NO_COVERAGE
        String toolVersion = version != null ? version : DEV_VERSION;
489 2 1. writeGapReport : removed conditional - replaced equality check with false → NO_COVERAGE
2. writeGapReport : removed conditional - replaced equality check with true → NO_COVERAGE
        Path target = cliConfig.gapReportFile() != null
490
                ? cliConfig.gapReportFile()
491
                : Path.of(GapFacade.DEFAULT_GAP_REPORT_FILENAME);
492
        try {
493
            handle.write(toolVersion, target);
494
        } catch (IOException e) {
495
            if (LOG.isLoggable(Level.WARNING)) {
496
                LOG.log(Level.WARNING,
497
                        "Could not write gap report: {0}", e.getMessage());
498
            }
499
        }
500
    }
501
502
    /**
503
     * Prepares an evidence-report handle when {@code -evidence-report} is active.
504
     *
505
     * @param cliConfig parsed CLI configuration
506
     * @return prepared handle, or {@code null} when evidence-report mode is not active
507
     */
508
    private static EvidenceReportFacade.Handle prepareEvidenceReportHandle(CliConfig cliConfig) {
509 2 1. prepareEvidenceReportHandle : removed conditional - replaced equality check with true → SURVIVED
2. prepareEvidenceReportHandle : removed conditional - replaced equality check with false → SURVIVED
        if (!cliConfig.evidenceReport()) {
510
            return null;
511
        }
512 1 1. prepareEvidenceReportHandle : replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareEvidenceReportHandle → NO_COVERAGE
        return EvidenceReportFacade.prepare(cliConfig.minConfidence(), cliConfig.paths());
513
    }
514
515
    /**
516
     * Prepares an attestation collector when {@code -attest} is active.
517
     *
518
     * @param cliConfig parsed CLI configuration
519
     * @return prepared handle, or {@code null} when attest mode is not active
520
     */
521
    private static AttestFacade.Handle prepareAttestHandle(CliConfig cliConfig) {
522 2 1. prepareAttestHandle : removed conditional - replaced equality check with true → SURVIVED
2. prepareAttestHandle : removed conditional - replaced equality check with false → SURVIVED
        if (!cliConfig.attest()) {
523
            return null;
524
        }
525 1 1. prepareAttestHandle : replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareAttestHandle → NO_COVERAGE
        return AttestFacade.prepare(cliConfig.minConfidence(), cliConfig.paths());
526
    }
527
528
    /**
529
     * Writes the security evidence report. Errors are logged and swallowed so
530
     * a report-write failure never demotes a successful scan.
531
     *
532
     * @param cliConfig parsed CLI configuration
533
     * @param version   resolved tool version
534
     * @param handle    prepared evidence-report handle; never {@code null}
535
     */
536
    private static void writeEvidenceReport(CliConfig cliConfig, String version,
537
            EvidenceReportFacade.Handle handle) {
538 2 1. writeEvidenceReport : removed conditional - replaced equality check with false → NO_COVERAGE
2. writeEvidenceReport : removed conditional - replaced equality check with true → NO_COVERAGE
        String toolVersion = version != null ? version : DEV_VERSION;
539 2 1. writeEvidenceReport : removed conditional - replaced equality check with true → NO_COVERAGE
2. writeEvidenceReport : removed conditional - replaced equality check with false → NO_COVERAGE
        Path target = cliConfig.evidenceReportFile() != null
540
                ? cliConfig.evidenceReportFile()
541
                : Path.of(EvidenceReportFacade.DEFAULT_REPORT_FILENAME);
542
        try {
543
            handle.write(toolVersion, target);
544
        } catch (IOException e) {
545
            if (LOG.isLoggable(Level.WARNING)) {
546
                LOG.log(Level.WARNING,
547
                        "Could not write evidence report: {0}", e.getMessage());
548
            }
549
        }
550
    }
551
552
    private static void writeAttestation(CliConfig cliConfig, String version,
553
            AttestFacade.Handle handle) {
554 2 1. writeAttestation : removed conditional - replaced equality check with false → NO_COVERAGE
2. writeAttestation : removed conditional - replaced equality check with true → NO_COVERAGE
        String toolVersion = version != null ? version : DEV_VERSION;
555 2 1. writeAttestation : removed conditional - replaced equality check with true → NO_COVERAGE
2. writeAttestation : removed conditional - replaced equality check with false → NO_COVERAGE
        Path target = cliConfig.attestFile() != null
556
                ? cliConfig.attestFile()
557
                : Path.of(AttestFacade.DEFAULT_ATTEST_FILENAME);
558
        try {
559
            handle.write(toolVersion, target);
560
        } catch (IOException e) {
561
            if (LOG.isLoggable(Level.WARNING)) {
562
                LOG.log(Level.WARNING,
563
                        "Could not write attestation manifest: {0}", e.getMessage());
564
            }
565
        }
566
    }
567
568
    /**
569
     * Writes a reproducibility receipt for the just-completed scan.
570
     *
571
     * <p>
572
     * Failures are logged at WARNING level and swallowed so a receipt-write
573
     * error never turns a successful scan into a non-zero exit code.
574
     * </p>
575
     *
576
     * @param cliConfig parsed CLI configuration whose
577
     *                  {@link CliConfig#emitReceipt()} is already known to be
578
     *                  {@code true}
579
     * @param version   tool version resolved from the JAR manifest; {@code null}
580
     *                  resolves to {@code "dev"}
581
     */
582
    private static void emitReceipt(CliConfig cliConfig, String version) {
583 2 1. emitReceipt : removed conditional - replaced equality check with false → SURVIVED
2. emitReceipt : removed conditional - replaced equality check with true → KILLED
        String toolVersion = version != null ? version : DEV_VERSION;
584
        String modeName = cliConfig.outputMode().name();
585
        try {
586
            ReceiptFacade.emit(cliConfig, toolVersion, modeName);
587
        } catch (IOException e) {
588
            if (LOG.isLoggable(Level.WARNING)) {
589
                LOG.log(Level.WARNING,
590
                        "Could not write reproducibility receipt: {0}", e.getMessage());
591
            }
592
        }
593
    }
594
595
    /**
596
     * Builds a composite extra sink from all active side-channel handles.
597
     * When only one handle is active its own sink is returned directly;
598
     * when multiple are active their {@code record} calls are fanned out in order.
599
     *
600
     * @param coverageHandle  coverage handle; may be {@code null}
601
     * @param gapHandle       gap-report handle; may be {@code null}
602
     * @param evidenceHandle  evidence-report handle; may be {@code null}
603
     * @param attestHandle    attestation handle; may be {@code null}
604
     * @return optional sink; empty when no feature is active
605
     */
606
    @SuppressWarnings("PMD.NPathComplexity")
607
    private static java.util.Optional<org.egothor.methodatlas.emit.TestMethodSink> buildExtraSink(
608
            CoverageFacade.Handle coverageHandle, GapFacade.Handle gapHandle,
609
            EvidenceReportFacade.Handle evidenceHandle, AttestFacade.Handle attestHandle) {
610
        org.egothor.methodatlas.emit.TestMethodSink coverageSink =
611 2 1. buildExtraSink : removed conditional - replaced equality check with false → KILLED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
                coverageHandle == null ? null : coverageHandle.asSink();
612
        org.egothor.methodatlas.emit.TestMethodSink gapSink =
613 2 1. buildExtraSink : removed conditional - replaced equality check with true → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with false → KILLED
                gapHandle == null ? null : gapHandle.asSink();
614
        org.egothor.methodatlas.emit.TestMethodSink evidenceSink =
615 2 1. buildExtraSink : removed conditional - replaced equality check with true → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with false → KILLED
                evidenceHandle == null ? null : evidenceHandle.asSink();
616
        org.egothor.methodatlas.emit.TestMethodSink attestSink =
617 2 1. buildExtraSink : removed conditional - replaced equality check with true → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with false → KILLED
                attestHandle == null ? null : attestHandle.asSink();
618
        java.util.List<org.egothor.methodatlas.emit.TestMethodSink> active = new java.util.ArrayList<>();
619 2 1. buildExtraSink : removed conditional - replaced equality check with false → KILLED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
        if (coverageSink != null) {
620
            active.add(coverageSink);
621
        }
622 2 1. buildExtraSink : removed conditional - replaced equality check with false → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
        if (gapSink != null) {
623
            active.add(gapSink);
624
        }
625 2 1. buildExtraSink : removed conditional - replaced equality check with false → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
        if (evidenceSink != null) {
626
            active.add(evidenceSink);
627
        }
628 2 1. buildExtraSink : removed conditional - replaced equality check with false → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
        if (attestSink != null) {
629
            active.add(attestSink);
630
        }
631 2 1. buildExtraSink : removed conditional - replaced equality check with false → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with true → KILLED
        if (active.isEmpty()) {
632
            return java.util.Optional.empty();
633
        }
634 2 1. buildExtraSink : removed conditional - replaced equality check with true → SURVIVED
2. buildExtraSink : removed conditional - replaced equality check with false → SURVIVED
        if (active.size() == SINGLE_SINK) {
635 1 1. buildExtraSink : replaced return value with Optional.empty for org/egothor/methodatlas/MethodAtlasApp::buildExtraSink → KILLED
            return java.util.Optional.of(active.get(0));
636
        }
637 1 1. buildExtraSink : replaced return value with Optional.empty for org/egothor/methodatlas/MethodAtlasApp::buildExtraSink → NO_COVERAGE
        return java.util.Optional.of(
638
                (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) -> {
639
                    for (org.egothor.methodatlas.emit.TestMethodSink sink : active) {
640
                        sink.record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
641
                    }
642
                });
643
    }
644
645
    private static int runWithScanRun(PrintWriter out, CliConfig cliConfig,
646
            CoverageFacade.Handle coverageHandle, GapFacade.Handle gapHandle,
647
            EvidenceReportFacade.Handle evidenceHandle, AttestFacade.Handle attestHandle) throws IOException {
648
        AiRuntimeBuilder aiRuntimeBuilder = new AiRuntimeBuilder();
649
        ClassificationOverride override = new OverrideLoader().load(cliConfig.overrideFile());
650
        AiResultCache aiCache = aiRuntimeBuilder.buildCache(cliConfig.aiCacheFile());
651
652
        TestDiscoveryConfig discoveryConfig =
653
                new TestDiscoveryConfig(cliConfig.fileSuffixes(), cliConfig.testMarkers(), cliConfig.properties());
654
655
        // One PluginLoader + one ScanOrchestrator are shared by every command in
656
        // this run; both are stateless and the providers they resolve are owned
657
        // (and closed) by the command that requested them. When -emit-coverage or
658
        // -gap-report is active the orchestrator carries the extra sink(s) as a
659
        // fan-out — every command mode sees the same fan-out automatically.
660
        PluginLoader pluginLoader = new PluginLoader();
661
        java.util.Optional<org.egothor.methodatlas.emit.TestMethodSink> extraSink =
662
                buildExtraSink(coverageHandle, gapHandle, evidenceHandle, attestHandle);
663 2 1. runWithScanRun : removed conditional - replaced equality check with true → SURVIVED
2. runWithScanRun : removed conditional - replaced equality check with false → KILLED
        CommentRedaction redaction = cliConfig.aiOptions().redactComments()
664
                ? new CommentRedaction(pluginLoader.loadRedactors(discoveryConfig))
665
                : CommentRedaction.none();
666
        ScanOrchestrator scanOrchestrator = new ScanOrchestrator(pluginLoader, extraSink,
667
                cliConfig.parallelAi(), redaction);
668
669
        // Manual prepare phase: write AI prompt work files; no CSV output.
670 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.manualMode() instanceof ManualMode.Prepare prepare) {
671 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED
            return new ManualPrepareCommand(prepare, cliConfig, discoveryConfig, pluginLoader).execute(out);
672
        }
673
674
        // Determine AI engine: manual consume reads from files; normal mode calls APIs.
675
        AiSuggestionEngine aiEngine;
676 2 1. runWithScanRun : removed conditional - replaced equality check with true → KILLED
2. runWithScanRun : removed conditional - replaced equality check with false → KILLED
        if (cliConfig.manualMode() instanceof ManualMode.Consume consume) {
677
            aiEngine = new ManualConsumeEngine(consume.responseDir());
678
        } else {
679
            aiEngine = aiRuntimeBuilder.buildEngine(cliConfig.aiOptions());
680
        }
681
682
        // Apply-tags-from-csv mode: apply reviewed CSV decisions to source files.
683 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.applyTagsFromCsvFile() != null) {
684 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED
            return new ApplyTagsFromCsvCommand(cliConfig, discoveryConfig, pluginLoader).execute(out);
685
        }
686
687
        // Apply-tags mode: annotate source files; no report emitted.
688 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.applyTags()) {
689 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → SURVIVED
            return new ApplyTagsCommand(cliConfig, discoveryConfig, aiEngine, override, aiCache,
690
                    pluginLoader, scanOrchestrator).execute(out);
691
        }
692
693
        // Evidence-pack mode: produces a tamper-evident self-contained directory
694
        // (SARIF + CSV + manifest + optional ZeroEcho signature). Must precede the
695
        // SARIF/JSON/GitHub-annotation branches because those formats are
696
        // re-used internally; the dispatch decision is owned by this command.
697 2 1. runWithScanRun : removed conditional - replaced equality check with false → SURVIVED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.evidencePackFramework() != null) {
698 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → NO_COVERAGE
            return dispatchEvidencePack(cliConfig, discoveryConfig, aiEngine, override, aiCache,
699
                    scanOrchestrator);
700
        }
701
702
        // SARIF mode: buffer all records; write JSON once after the scan completes.
703 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.outputMode() == OutputMode.SARIF) {
704 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → SURVIVED
            return new SarifCommand(cliConfig, discoveryConfig, aiEngine, override, aiCache,
705
                    scanOrchestrator).execute(out);
706
        }
707
708
        // JSON mode: buffer all records; write flat JSON array after scan completes.
709 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.outputMode() == OutputMode.JSON) {
710 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → SURVIVED
            return new JsonCommand(cliConfig, discoveryConfig, aiEngine, override, aiCache,
711
                    pluginLoader, scanOrchestrator).execute(out);
712
        }
713
714
        // GitHub Annotations mode: emit ::notice/::warning workflow commands.
715 2 1. runWithScanRun : removed conditional - replaced equality check with false → KILLED
2. runWithScanRun : removed conditional - replaced equality check with true → KILLED
        if (cliConfig.outputMode() == OutputMode.GITHUB_ANNOTATIONS) {
716 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → SURVIVED
            return new GitHubAnnotationsCommand(cliConfig, discoveryConfig, aiEngine, override, aiCache,
717
                    scanOrchestrator).execute(out);
718
        }
719
720
        // CSV / PLAIN mode: emit incrementally (default).
721 1 1. runWithScanRun : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED
        return new ScanCommand(cliConfig, discoveryConfig, aiEngine, override, aiCache,
722
                pluginLoader, scanOrchestrator).execute(out);
723
    }
724
725
    /**
726
     * Parses the {@code -evidence-pack} framework token, assembles
727
     * {@link EvidencePackOptions}, runs the {@link EvidencePackCommand}, and
728
     * prints a one-line summary to {@code stderr}. The signing key is read from
729
     * an encrypted ZeroEcho keyring; its password comes from a password file, an
730
     * environment variable, or an interactive prompt — never from the command line.
731
     *
732
     * @param cliConfig         parsed CLI configuration
733
     * @param discoveryConfig   discovery configuration forwarded to providers
734
     * @param aiEngine          AI engine, or {@code null}
735
     * @param override          classification override
736
     * @param aiCache           AI result cache
737
     * @param scanOrchestrator  shared scan orchestrator
738
     * @return exit code propagated from {@link EvidencePackCommand#execute()}
739
     *         or {@value #EXIT_BAD_FRAMEWORK} when the framework token is invalid
740
     * @throws IOException if the underlying scan or I/O fails
741
     */
742
    private static int dispatchEvidencePack(CliConfig cliConfig, TestDiscoveryConfig discoveryConfig,
743
            AiSuggestionEngine aiEngine, ClassificationOverride override, AiResultCache aiCache,
744
            ScanOrchestrator scanOrchestrator) throws IOException {
745
        EvidenceFramework framework;
746
        try {
747
            framework = EvidenceFramework.parse(cliConfig.evidencePackFramework());
748
        } catch (IllegalArgumentException e) {
749
            System.err.println(e.getMessage());
750 1 1. dispatchEvidencePack : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::dispatchEvidencePack → NO_COVERAGE
            return EXIT_BAD_FRAMEWORK;
751
        }
752
        EvidencePackOptions packOptions = new EvidencePackOptions(
753
                framework,
754
                cliConfig.evidencePackDir(),
755
                cliConfig.evidencePackOverwrite(),
756
                cliConfig.evidencePackKeyringFile(),
757
                cliConfig.evidencePackKeyringEnv(),
758
                cliConfig.evidencePackPasswordFile(),
759
                cliConfig.evidencePackPasswordEnv(),
760
                cliConfig.evidencePackKeyAlias(),
761
                cliConfig.evidencePackSignAlgo());
762
        EvidencePackCommand command = new EvidencePackCommand(cliConfig, packOptions, discoveryConfig,
763
                aiEngine, override, aiCache, scanOrchestrator);
764
        int exit = command.execute();
765
        announcePack(command, packOptions);
766 1 1. dispatchEvidencePack : replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::dispatchEvidencePack → NO_COVERAGE
        return exit;
767
    }
768
769
    /**
770
     * Prints the one-line outcome banner that documents how the pack was
771
     * produced. Always written to {@code System.err} so the stdout stream
772
     * remains usable for piping.
773
     *
774
     * @param command     executed command, used to retrieve the absolute path
775
     * @param packOptions options driving the command
776
     */
777
    private static void announcePack(EvidencePackCommand command, EvidencePackOptions packOptions) {
778
        String absolute = command.outputDir().toString();
779 4 1. announcePack : removed conditional - replaced equality check with false → NO_COVERAGE
2. announcePack : removed conditional - replaced equality check with true → NO_COVERAGE
3. announcePack : removed conditional - replaced equality check with false → NO_COVERAGE
4. announcePack : removed conditional - replaced equality check with true → NO_COVERAGE
        if (packOptions.keyringFile() == null && packOptions.keyringEnv() == null) {
780
            System.err.println("evidence pack written to " + absolute
781
                    + " (unsigned — no keyring supplied)");
782
            return;
783
        }
784
        String algo = command.signatureAlgorithm();
785 2 1. announcePack : removed conditional - replaced equality check with true → NO_COVERAGE
2. announcePack : removed conditional - replaced equality check with false → NO_COVERAGE
        if (algo != null) {
786
            System.err.println("evidence pack written to " + absolute
787
                    + " (signed: " + algo + ")");
788
        } else {
789
            System.err.println("evidence pack written to " + absolute
790
                    + " (WARNING: signing failed — check log)");
791
        }
792
    }
793
}

Mutations

258

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

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

262

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

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

293

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

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

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

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

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

6.6
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppTest]/[method:help_longAndShortAliasesAccepted()]
removed conditional - replaced equality check with true → KILLED

302

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

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

3.3
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppTest]/[method:testAnnotation_customAnnotationIsRecognised(java.nio.file.Path)]
changed conditional boundary → KILLED

303

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

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

304

1.1
Location : run
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

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

3.3
Location : run
Killed by : none
Replaced integer addition with subtraction → SURVIVED Covering tests

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

308

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

310

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

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

311

1.1
Location : run
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE

313

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

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

314

1.1
Location : run
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE

316

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

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

317

1.1
Location : run
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE

319

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

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

320

1.1
Location : run
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → NO_COVERAGE

325

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

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

328

1.1
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:emitCoverage_withoutMapping_exitsWithCodeTwoAndPrintsHelpfulError(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED

338

1.1
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:emitCoverage_withMissingMapping_exitsWithCodeTwo(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED

359

1.1
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsFromCsvTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsFromCsvTest]/[method:applyTagsFromCsv_mismatchLimitReturnsOne(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED

364

1.1
Location : run
Killed by : org.egothor.methodatlas.MethodAtlasAppRedactCommentsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppRedactCommentsTest]/[method:manualPrepare_withRedactAndNoRedactorForLanguage_abortsAndWritesNothing(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::run → KILLED

384

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

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

387

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

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

390

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

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

393

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

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

396

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

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

411

1.1
Location : buildDiffCommand
Killed by : org.egothor.methodatlas.DeltaReportTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.DeltaReportTest]/[method:app_diffFlag_producesReport(java.nio.file.Path)]
Replaced integer addition with subtraction → KILLED

412

1.1
Location : buildDiffCommand
Killed by : org.egothor.methodatlas.DeltaReportTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.DeltaReportTest]/[method:app_diffFlag_producesReport(java.nio.file.Path)]
Replaced integer addition with subtraction → KILLED

415

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

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

3.3
Location : buildDiffCommand
Killed by : org.egothor.methodatlas.DeltaReportTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.DeltaReportTest]/[method:app_diffFlag_producesReport(java.nio.file.Path)]
changed conditional boundary → KILLED

416

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

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

418

1.1
Location : buildDiffCommand
Killed by : none
removed conditional - replaced comparison check with true → NO_COVERAGE

2.2
Location : buildDiffCommand
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : buildDiffCommand
Killed by : none
removed conditional - replaced comparison check with false → NO_COVERAGE

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

5.5
Location : buildDiffCommand
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

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

419

1.1
Location : buildDiffCommand
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

422

1.1
Location : buildDiffCommand
Killed by : org.egothor.methodatlas.DeltaReportTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.DeltaReportTest]/[method:app_diffFlag_producesReport(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::buildDiffCommand → KILLED

435

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

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

438

1.1
Location : prepareCoverageHandle
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/MethodAtlasApp::prepareCoverageHandle → KILLED

451

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

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

452

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

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

472

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

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

475

1.1
Location : prepareGapHandle
Killed by : none
replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareGapHandle → NO_COVERAGE

488

1.1
Location : writeGapReport
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

2.2
Location : writeGapReport
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

489

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

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

509

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

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

512

1.1
Location : prepareEvidenceReportHandle
Killed by : none
replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareEvidenceReportHandle → NO_COVERAGE

522

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

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

525

1.1
Location : prepareAttestHandle
Killed by : none
replaced return value with null for org/egothor/methodatlas/MethodAtlasApp::prepareAttestHandle → NO_COVERAGE

538

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

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

539

1.1
Location : writeEvidenceReport
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

2.2
Location : writeEvidenceReport
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

554

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

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

555

1.1
Location : writeAttestation
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

2.2
Location : writeAttestation
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

583

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

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

611

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

2.2
Location : buildExtraSink
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

613

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

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

615

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

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

617

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

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

619

1.1
Location : buildExtraSink
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 false → KILLED

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

622

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

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

625

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

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

628

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

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

631

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

2.2
Location : buildExtraSink
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

634

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

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

635

1.1
Location : buildExtraSink
Killed by : org.egothor.methodatlas.MethodAtlasAppCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppCoverageTest]/[method:accessControlAnnotation_mapsToAsvs4Dot1Dot1(java.nio.file.Path)]
replaced return value with Optional.empty for org/egothor/methodatlas/MethodAtlasApp::buildExtraSink → KILLED

637

1.1
Location : buildExtraSink
Killed by : none
replaced return value with Optional.empty for org/egothor/methodatlas/MethodAtlasApp::buildExtraSink → NO_COVERAGE

663

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

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

670

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

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

671

1.1
Location : runWithScanRun
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:manualPrepare_unparseableFile_returnsExitCode1(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED

676

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

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

683

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

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

684

1.1
Location : runWithScanRun
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsFromCsvTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsFromCsvTest]/[method:applyTagsFromCsv_mismatchLimitReturnsOne(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED

688

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

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

689

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

697

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

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

698

1.1
Location : runWithScanRun
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → NO_COVERAGE

703

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

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

704

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

709

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

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

710

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

715

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

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

716

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

721

1.1
Location : runWithScanRun
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_unparseableFile_returnsExitCode1(java.nio.file.Path)]
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::runWithScanRun → KILLED

750

1.1
Location : dispatchEvidencePack
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::dispatchEvidencePack → NO_COVERAGE

766

1.1
Location : dispatchEvidencePack
Killed by : none
replaced int return with 0 for org/egothor/methodatlas/MethodAtlasApp::dispatchEvidencePack → NO_COVERAGE

779

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

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

3.3
Location : announcePack
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

4.4
Location : announcePack
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

785

1.1
Location : announcePack
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

2.2
Location : announcePack
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.30.1 support