import java.nio.file.*; import java.util.*; import java.util.regex.*; import java.util.stream.*; /** * Cross-check the quoted DebugLog lines in docs/android_app/real_device_checklist.md * against the Kotlin sources, so a verifier can confirm the checklist describes * logs the code can actually produce. * * WHY NOT A PLAIN SEARCH: the sources build messages by interpolation * ("$name write=$n/${packet.size}") while the checklist shows instantiated * examples ("GetParameter1 write=4/4"). A literal search therefore produces * false negatives. This tool instead (1) tries the literal with * removed, then (2) falls back to requiring the remaining identifier-ish words * to appear somewhere in the sources. * * A "needs review" line means the whole message is instance data (the template * lives in one string literal and the command name is a call-site argument) — * those must be traced by hand to the call site. Both categories were manually * resolved for the 2026-09-11 run (see verification_guide.md §3 Phase A). * * Usage: * java CheckStrings.java docs/android_app/real_device_checklist.md android/app/src/main */ public class CheckStrings { public static void main(String[] a) throws Exception { String md = Files.readString(Paths.get(a[0]), java.nio.charset.StandardCharsets.UTF_8); StringBuilder src = new StringBuilder(); try (Stream s = Files.walk(Paths.get(a[1]))) { List kt = s.filter(x -> x.toString().endsWith(".kt")).collect(Collectors.toList()); for (Path p : kt) src.append(Files.readString(p, java.nio.charset.StandardCharsets.UTF_8)).append('\n'); } String all = src.toString(); // only lines that show a DebugLog tag: [usb] [vm] [session] [cmd] [stream] [pip] [remote] [crash] Matcher m = Pattern.compile("`(\\[(?:usb|vm|session|cmd|stream|pip|remote|crash)\\][^`]*)`").matcher(md); int exact = 0, skeleton = 0, miss = 0; Set seen = new LinkedHashSet<>(); List misses = new ArrayList<>(); while (m.find()) { String line = m.group(1); if (!seen.add(line)) continue; // the tag is a separate DebugLog argument in the source String msg = line.replaceFirst("^\\[[a-z]+\\]\\s*", ""); String literal = msg.replaceAll("<[^>]*>", "").trim(); if (literal.length() >= 6 && all.contains(literal)) { exact++; continue; } String skel = msg .replaceAll("<[^>]*>", " ") .replaceAll("0x[0-9A-Fa-f]+", " ") .replaceAll("\\b\\d+(\\.\\d+)?\\b", " ") .replaceAll("[A-Za-z]*\\d[A-Za-z0-9]*", " ") .replaceAll("[^\\x20-\\x7E\\u4e00-\\u9fff]+", " "); List keep = new ArrayList<>(); for (String w : skel.trim().split("\\s+")) { if (w.length() >= 3 && w.matches("[A-Za-z_:.\\-\\[\\]()/]+")) keep.add(w); } boolean found = !keep.isEmpty(); for (String w : keep) if (!all.contains(w)) found = false; if (found) skeleton++; else { miss++; misses.add(line + " [probe: " + String.join(" ", keep) + "]"); } } System.out.println("exact literal match : " + exact); System.out.println("skeleton match : " + skeleton); System.out.println("needs human review : " + miss); if (!misses.isEmpty()) { System.out.println(); for (String s : misses) System.out.println(" ? " + s); } } }