| 1 | package org.egothor.methodatlas.command; | |
| 2 | ||
| 3 | import java.io.IOException; | |
| 4 | import java.io.PrintWriter; | |
| 5 | import java.io.UncheckedIOException; | |
| 6 | import java.nio.file.Files; | |
| 7 | import java.nio.file.Path; | |
| 8 | import java.util.EnumMap; | |
| 9 | import java.util.List; | |
| 10 | import java.util.Map; | |
| 11 | ||
| 12 | import org.egothor.methodatlas.ai.PromptTemplateKind; | |
| 13 | import org.egothor.methodatlas.ai.PromptTemplateSet; | |
| 14 | import org.egothor.methodatlas.ai.PromptTemplateValidator; | |
| 15 | ||
| 16 | /** | |
| 17 | * Utility CLI mode ({@code -check-prompts}) that validates prompt templates and | |
| 18 | * prints their SHA-256 checksums without running a scan. | |
| 19 | * | |
| 20 | * <p> | |
| 21 | * For each {@link PromptTemplateKind} it reports whether the effective template | |
| 22 | * (the operator's override, if supplied via the matching flag, otherwise the | |
| 23 | * built-in default) is structurally valid, lists any problems, and prints the | |
| 24 | * checksum that a reproducibility receipt would record. It exits with status | |
| 25 | * {@code 1} if any template is invalid, making it suitable as a CI pre-flight | |
| 26 | * gate; otherwise {@code 0}. | |
| 27 | * </p> | |
| 28 | * | |
| 29 | * <p> | |
| 30 | * Validation covers placeholder correctness and the survival of the JSON | |
| 31 | * structural anchor the response parser needs; it cannot verify that a model will | |
| 32 | * follow the instructions (see {@link PromptTemplateValidator}). | |
| 33 | * </p> | |
| 34 | * | |
| 35 | * @since 4.1.0 | |
| 36 | */ | |
| 37 | public final class CheckPromptsCommand implements Command { | |
| 38 | ||
| 39 | /** Flag that selects this utility mode. */ | |
| 40 | public static final String FLAG_CHECK_PROMPTS = "-check-prompts"; | |
| 41 | ||
| 42 | /** Flag supplying a custom method-classification template file. */ | |
| 43 | public static final String FLAG_CLASSIFICATION_PROMPT = "-classification-prompt"; | |
| 44 | ||
| 45 | /** Flag supplying a custom folded credential-triage appendix template file. */ | |
| 46 | public static final String FLAG_TRIAGE_PROMPT = "-triage-prompt"; | |
| 47 | ||
| 48 | /** Flag supplying a custom standalone credential-triage template file. */ | |
| 49 | public static final String FLAG_DEDICATED_TRIAGE_PROMPT = "-dedicated-triage-prompt"; | |
| 50 | ||
| 51 | private final Map<PromptTemplateKind, Path> overrides; | |
| 52 | ||
| 53 | /** | |
| 54 | * Creates a command for the supplied template overrides. | |
| 55 | * | |
| 56 | * @param overrides map of kind to override file; kinds absent from the map use | |
| 57 | * the built-in default; must not be {@code null} | |
| 58 | */ | |
| 59 | /* default */ CheckPromptsCommand(Map<PromptTemplateKind, Path> overrides) { | |
| 60 | // Build via putAll rather than the EnumMap copy constructor, which rejects an | |
| 61 | // empty source map (it cannot infer the enum type from no entries). | |
| 62 | Map<PromptTemplateKind, Path> copy = new EnumMap<>(PromptTemplateKind.class); | |
| 63 | copy.putAll(overrides); | |
| 64 | this.overrides = copy; | |
| 65 | } | |
| 66 | ||
| 67 | /** | |
| 68 | * Builds a command by scanning raw command-line arguments for the three | |
| 69 | * prompt-override flags. | |
| 70 | * | |
| 71 | * @param args raw command-line arguments; must not be {@code null} | |
| 72 | * @return a configured command; never {@code null} | |
| 73 | */ | |
| 74 | public static CheckPromptsCommand fromArgs(String... args) { | |
| 75 | Map<PromptTemplateKind, Path> overrides = new EnumMap<>(PromptTemplateKind.class); | |
| 76 | putIfPresent(overrides, args, FLAG_CLASSIFICATION_PROMPT, PromptTemplateKind.CLASSIFICATION); | |
| 77 | putIfPresent(overrides, args, FLAG_TRIAGE_PROMPT, PromptTemplateKind.TRIAGE_APPENDIX); | |
| 78 | putIfPresent(overrides, args, FLAG_DEDICATED_TRIAGE_PROMPT, PromptTemplateKind.DEDICATED_TRIAGE); | |
| 79 |
1
1. fromArgs : replaced return value with null for org/egothor/methodatlas/command/CheckPromptsCommand::fromArgs → KILLED |
return new CheckPromptsCommand(overrides); |
| 80 | } | |
| 81 | ||
| 82 | private static void putIfPresent(Map<PromptTemplateKind, Path> map, String[] args, String flag, | |
| 83 | PromptTemplateKind kind) { | |
| 84 |
4
1. putIfPresent : changed conditional boundary → SURVIVED 2. putIfPresent : removed conditional - replaced comparison check with false → KILLED 3. putIfPresent : removed conditional - replaced comparison check with true → KILLED 4. putIfPresent : Replaced integer subtraction with addition → KILLED |
for (int i = 0; i < args.length - 1; i++) { |
| 85 |
2
1. putIfPresent : removed conditional - replaced equality check with true → SURVIVED 2. putIfPresent : removed conditional - replaced equality check with false → KILLED |
if (flag.equals(args[i])) { |
| 86 |
1
1. putIfPresent : Replaced integer addition with subtraction → KILLED |
map.put(kind, Path.of(args[i + 1])); |
| 87 | return; | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | /** | |
| 93 | * Validates each template kind and prints a report. | |
| 94 | * | |
| 95 | * @param out writer receiving the report; never {@code null} | |
| 96 | * @return {@code 0} when every template is valid; {@code 1} otherwise | |
| 97 | * @throws IOException never thrown directly; per-file read failures are reported | |
| 98 | * inline as validation failures | |
| 99 | */ | |
| 100 | @Override | |
| 101 | public int execute(PrintWriter out) throws IOException { | |
| 102 | boolean allValid = true; | |
| 103 | for (PromptTemplateKind kind : PromptTemplateKind.values()) { | |
| 104 |
1
1. execute : Replaced bitwise AND with OR → KILLED |
allValid &= reportKind(out, kind); |
| 105 | } | |
| 106 | out.println(); | |
| 107 |
2
1. execute : removed conditional - replaced equality check with false → KILLED 2. execute : removed conditional - replaced equality check with true → KILLED |
out.println(allValid |
| 108 | ? "All prompt templates are valid." | |
| 109 | : "One or more prompt templates are INVALID."); | |
| 110 |
3
1. execute : removed conditional - replaced equality check with true → KILLED 2. execute : removed conditional - replaced equality check with false → KILLED 3. execute : replaced int return with 0 for org/egothor/methodatlas/command/CheckPromptsCommand::execute → KILLED |
return allValid ? 0 : 1; |
| 111 | } | |
| 112 | ||
| 113 | private boolean reportKind(PrintWriter out, PromptTemplateKind kind) { | |
| 114 | Path file = overrides.get(kind); | |
| 115 |
2
1. reportKind : removed conditional - replaced equality check with true → KILLED 2. reportKind : removed conditional - replaced equality check with false → KILLED |
String source = file == null ? "built-in default" : file.toString(); |
| 116 | String body; | |
| 117 | try { | |
| 118 |
2
1. reportKind : removed conditional - replaced equality check with false → KILLED 2. reportKind : removed conditional - replaced equality check with true → KILLED |
body = file == null ? PromptTemplateSet.defaults().get(kind) : Files.readString(file); |
| 119 | } catch (IOException | UncheckedIOException e) { | |
| 120 | out.println(kind + " [" + source + "]: FAIL"); | |
| 121 | out.println(" - cannot read file: " + e.getMessage()); | |
| 122 |
1
1. reportKind : replaced boolean return with true for org/egothor/methodatlas/command/CheckPromptsCommand::reportKind → KILLED |
return false; |
| 123 | } | |
| 124 | ||
| 125 | List<String> problems = PromptTemplateValidator.validate(kind, body); | |
| 126 | boolean ok = problems.isEmpty(); | |
| 127 |
2
1. reportKind : removed conditional - replaced equality check with false → KILLED 2. reportKind : removed conditional - replaced equality check with true → KILLED |
out.println(kind + " [" + source + "]: " + (ok ? "PASS" : "FAIL")); |
| 128 | out.println(" sha256: " + PromptTemplateSet.defaults().with(kind, body).hash(kind)); | |
| 129 | for (String problem : problems) { | |
| 130 | out.println(" - " + problem); | |
| 131 | } | |
| 132 |
2
1. reportKind : replaced boolean return with true for org/egothor/methodatlas/command/CheckPromptsCommand::reportKind → KILLED 2. reportKind : replaced boolean return with false for org/egothor/methodatlas/command/CheckPromptsCommand::reportKind → KILLED |
return ok; |
| 133 | } | |
| 134 | } | |
Mutations | ||
| 79 |
1.1 |
|
| 84 |
1.1 2.2 3.3 4.4 |
|
| 85 |
1.1 2.2 |
|
| 86 |
1.1 |
|
| 104 |
1.1 |
|
| 107 |
1.1 2.2 |
|
| 110 |
1.1 2.2 3.3 |
|
| 115 |
1.1 2.2 |
|
| 118 |
1.1 2.2 |
|
| 122 |
1.1 |
|
| 127 |
1.1 2.2 |
|
| 132 |
1.1 2.2 |