@hasna/recordings 0.2.13 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/Dockerfile.package +4 -5
  2. package/README.md +113 -8
  3. package/bun.lock +2 -2
  4. package/dist/__tests__/helpers/installer-guard-execution.d.ts +22 -0
  5. package/dist/__tests__/helpers/installer-guard-execution.d.ts.map +1 -0
  6. package/dist/__tests__/helpers/installer-preflight.d.ts +22 -0
  7. package/dist/__tests__/helpers/installer-preflight.d.ts.map +1 -0
  8. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -1
  9. package/dist/__tests__/helpers/source-assertions.d.ts +169 -0
  10. package/dist/__tests__/helpers/source-assertions.d.ts.map +1 -0
  11. package/dist/__tests__/preload.d.ts +1 -4
  12. package/dist/__tests__/preload.d.ts.map +1 -1
  13. package/dist/cli/desktop-snapshot.d.ts +20 -0
  14. package/dist/cli/desktop-snapshot.d.ts.map +1 -0
  15. package/dist/cli/index.js +2078 -277
  16. package/dist/cli/macos-permissions.d.ts +72 -0
  17. package/dist/cli/macos-permissions.d.ts.map +1 -1
  18. package/dist/cli/macos-shortcut.d.ts +139 -0
  19. package/dist/cli/macos-shortcut.d.ts.map +1 -0
  20. package/dist/cli/trigger-probe.d.ts +172 -0
  21. package/dist/cli/trigger-probe.d.ts.map +1 -0
  22. package/dist/db/database.d.ts +9 -0
  23. package/dist/db/database.d.ts.map +1 -1
  24. package/dist/http/client.d.ts +10 -7
  25. package/dist/http/client.d.ts.map +1 -1
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +634 -49
  29. package/dist/lib/capture-probe.d.ts +216 -0
  30. package/dist/lib/capture-probe.d.ts.map +1 -0
  31. package/dist/lib/config.d.ts.map +1 -1
  32. package/dist/lib/macos-bundle.d.ts +50 -0
  33. package/dist/lib/macos-bundle.d.ts.map +1 -0
  34. package/dist/lib/persistence-probe.d.ts +208 -0
  35. package/dist/lib/persistence-probe.d.ts.map +1 -0
  36. package/dist/lib/recorder.d.ts.map +1 -1
  37. package/dist/lib/retired-deployment-modes.d.ts +24 -0
  38. package/dist/lib/retired-deployment-modes.d.ts.map +1 -0
  39. package/dist/lib/transcriber.d.ts +60 -0
  40. package/dist/lib/transcriber.d.ts.map +1 -1
  41. package/dist/mcp/index.js +140 -56
  42. package/dist/server/cloud-config.d.ts +25 -3
  43. package/dist/server/cloud-config.d.ts.map +1 -1
  44. package/dist/server/cloud-readiness.d.ts.map +1 -1
  45. package/dist/server/cloud.d.ts +8 -7
  46. package/dist/server/cloud.d.ts.map +1 -1
  47. package/dist/server/index.js +187 -79
  48. package/dist/server/migrate-command.d.ts.map +1 -1
  49. package/dist/server/serve.d.ts.map +1 -1
  50. package/dist/storage.d.ts +2 -2
  51. package/dist/storage.d.ts.map +1 -1
  52. package/dist/storage.js +108 -49
  53. package/dist/store.d.ts +1 -1
  54. package/dist/store.d.ts.map +1 -1
  55. package/dist/version.d.ts +1 -1
  56. package/dist/version.d.ts.map +1 -1
  57. package/package.json +25 -6
  58. package/scripts/build_native_fs_guard.sh +15 -2
  59. package/scripts/ci-linux-suite.ts +403 -0
  60. package/scripts/ci-native-build.ts +402 -0
  61. package/scripts/enforce_identity_migration.sh +110 -0
  62. package/scripts/generate-sdk.ts +1 -1
  63. package/scripts/install_macos_app.sh +138 -24
  64. package/scripts/macos_artifact.ts +205 -33
  65. package/scripts/native/realtime-settle-probe/README.md +39 -0
  66. package/scripts/native/realtime-settle-probe/main.swift +135 -0
  67. package/scripts/policy/local-only-approved-targets.txt +21 -0
  68. package/scripts/read_local_only_targets.sh +203 -0
  69. package/scripts/scan-artifact.ts +40 -0
  70. package/scripts/set-version.ts +145 -0
  71. package/scripts/vacuity-manifest-gen.ts +96 -0
  72. package/scripts/vacuity-manifests/enhancement-screen-mirror.tsv +44 -0
  73. package/scripts/vacuity-manifests/install-chain.tsv +13 -0
  74. package/scripts/vacuity-manifests/must-survive-controls.tsv +10 -0
  75. package/scripts/vacuity-manifests/reorder.tsv +4 -0
  76. package/scripts/vacuity-manifests/source-side.tsv +15 -0
  77. package/scripts/vacuity-manifests/variable-operands.tsv +16 -0
  78. package/scripts/vacuity-manifests/version-sites.tsv +61 -0
  79. package/scripts/vacuity-mutation-battery.ts +266 -0
  80. package/src/native/Recordings/App/MenuBarStatusView.swift +14 -7
  81. package/src/native/Recordings/App/RuntimeSmoke.swift +5 -1
  82. package/src/native/Recordings/App/SidebarView.swift +1 -1
  83. package/src/native/Recordings/Package.swift +24 -5
  84. package/src/native/Recordings/RecordingsLib/EnhancementScreen.swift +91 -0
  85. package/src/native/Recordings/RecordingsLib/FnKeyMonitor.swift +27 -4
  86. package/src/native/Recordings/RecordingsLib/Info.plist +2 -2
  87. package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +56 -3
  88. package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +13 -0
  89. package/src/native/Recordings/RecordingsLib/PasteDeliveryVerification.swift +460 -0
  90. package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +41 -7
  91. package/src/native/Recordings/RecordingsLib/RecordingAttemptAlert.swift +36 -0
  92. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +1071 -75
  93. package/src/native/Recordings/RecordingsLib/SettingsView.swift +47 -3
  94. package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +11 -6
  95. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +3 -3
  96. package/src/native/Recordings/RecordingsTests/EnhancementScreenTests.swift +191 -0
  97. package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +144 -4
  98. package/src/native/Recordings/RecordingsTests/PasteDeliveryVerificationTests.swift +463 -0
  99. package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +33 -20
  100. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +1 -1
  101. package/src/native/Recordings/RecordingsTests/RealtimeSettleBenchmark.swift +200 -0
  102. package/src/native/Recordings/RecordingsTests/RealtimeSettleBudgetTests.swift +80 -0
  103. package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +22 -4
  104. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +12 -6
  105. package/src/native/Recordings/RecordingsTests/RecordingEngineHomeIsolationTests.swift +67 -0
  106. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +12 -3
  107. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +207 -11
  108. package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +17 -6
  109. package/src/native/Recordings/RecordingsTests/TestHomeDirectory.swift +23 -0
  110. package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +7 -1
  111. package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +10 -10
  112. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +5 -1
  113. package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +1 -0
  114. package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +59 -3
  115. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +21 -2
  116. package/src/native/Recordings/Updater/ProtocolTests/ExpectationIntegrityTests.swift +60 -0
  117. package/src/native/Recordings/Updater/ProtocolTests/UpdateXPCInterfaceTests.swift +113 -0
  118. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +106 -5
  119. package/src/native/Recordings/build.sh +75 -9
  120. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Generates the test-literal mutation manifest for the vacuity battery (OPE41-00029).
3
+ *
4
+ * This exists instead of a committed manifest of byte offsets. Offsets are correct for exactly one
5
+ * revision of the test files: the first commit that adds a comment above a converted site shifts
6
+ * every offset below it, and the battery then reports MUTATION-NOT-APPLIED for rows that were fine.
7
+ * That happened — 66 of 176 rows went stale in one commit — and while the driver refuses to score a
8
+ * mutation it could not apply, a stale manifest still costs a full run and reads like a coverage
9
+ * hole. Generating from the current tree removes the failure mode rather than documenting it.
10
+ *
11
+ * For every `expectOrder` / `sliceBetween` / `sliceBetweenUnique` call site it emits one `corrupt`
12
+ * row per string-literal operand: append a sentinel to that literal in the test file, which is
13
+ * equivalent — from the assertion's point of view — to deleting the needle from the source, but
14
+ * needs no map from haystack variable to source path and leaves the script under test byte-identical
15
+ * so the run still succeeds and only the converted assertion can fail.
16
+ *
17
+ * Operands that are `const` variables rather than literals are NOT emitted here; those are covered
18
+ * by the source-side rows in scripts/vacuity-manifests/variable-operands.tsv.
19
+ *
20
+ * Usage: bun scripts/vacuity-manifest-gen.ts > /tmp/corrupt-sites.tsv
21
+ */
22
+ import { readFileSync } from "node:fs";
23
+ import { Glob } from "bun";
24
+
25
+ const HELPERS = /\b(expectOrder|sliceBetweenUnique|sliceBetween)\s*\(/g;
26
+
27
+ /** The nearest enclosing `it(...)` / `test(...)` name, used as the `-t` filter. */
28
+ function enclosingTest(source: string, position: number): string | undefined {
29
+ let best: string | undefined;
30
+ const pattern = /\b(?:it|test)\s*\(\s*(["'`])((?:\\.|(?!\1).)*)\1/g;
31
+ let match: RegExpExecArray | null;
32
+ while ((match = pattern.exec(source))) {
33
+ if (match.index >= position) break;
34
+ best = match[2];
35
+ }
36
+ return best;
37
+ }
38
+
39
+ /** Top-level string-literal arguments of a call, as [offset-of-inner-text, raw-inner-text]. */
40
+ function literalArguments(source: string, openParen: number): Array<[number, string]> {
41
+ const found: Array<[number, string]> = [];
42
+ let index = openParen + 1;
43
+ let depth = 1;
44
+ while (index < source.length && depth > 0) {
45
+ const character = source[index];
46
+ if (character === "(") depth++;
47
+ else if (character === ")") depth--;
48
+ else if (character === '"' || character === "'" || character === "`") {
49
+ const quote = character;
50
+ const start = ++index;
51
+ while (index < source.length && source[index] !== quote) {
52
+ if (source[index] === "\\") index++;
53
+ index++;
54
+ }
55
+ if (depth === 1) found.push([start, source.slice(start, index)]);
56
+ }
57
+ index++;
58
+ }
59
+ return found;
60
+ }
61
+
62
+ /** TSV cannot carry a real tab or newline. Backslash sequences pass through untouched, because the
63
+ * mutation compares the file's RAW bytes and `\n` inside a TS literal is two of them. */
64
+ function escapeForTsv(text: string): string {
65
+ return text.replace(/\t/g, "\\t").replace(/\n/g, "\\n");
66
+ }
67
+
68
+ const emitted = new Set<string>();
69
+ const lines: string[] = [
70
+ "# GENERATED by scripts/vacuity-manifest-gen.ts - do not hand-edit, regenerate.",
71
+ "# kind\ttestFile\ttestName\ttargetFile\toffset\tliteral",
72
+ ];
73
+
74
+ for (const path of new Glob("src/**/*.test.ts").scanSync(".")) {
75
+ // The guards suite calls the helpers on hand-built strings to prove they THROW. Its literals are
76
+ // fixtures, not needles into a real source file, so corrupting them measures nothing about the
77
+ // repo and only adds noise to the tally.
78
+ if (path.endsWith("source-assertions-guards.test.ts")) continue;
79
+ const source = readFileSync(path, "utf8");
80
+ HELPERS.lastIndex = 0;
81
+ let call: RegExpExecArray | null;
82
+ while ((call = HELPERS.exec(source))) {
83
+ const testName = enclosingTest(source, call.index);
84
+ if (!testName) continue;
85
+ for (const [offset, raw] of literalArguments(source, call.index + call[0].length - 1).slice(0, 2)) {
86
+ if (raw.length < 3) continue;
87
+ const key = `${path}:${offset}`;
88
+ if (emitted.has(key)) continue;
89
+ emitted.add(key);
90
+ lines.push(`corrupt\t${path}\t${testName}\t${path}\t${offset}\t${escapeForTsv(raw)}`);
91
+ }
92
+ }
93
+ }
94
+
95
+ console.log(lines.join("\n"));
96
+ console.error(`generated ${emitted.size} corrupt rows`);
@@ -0,0 +1,44 @@
1
+ # Re-derivation battery for the guards PR #35 introduces (todos 96546cc7).
2
+ #
3
+ # Scope: enhancer.test.ts (the PR's cross-language fixture twins) and
4
+ # enhancement-screen-mirror-contract.test.ts (added because the three claims below were
5
+ # enforced by nothing runnable on Linux). The three station-contended suites are NOT in scope;
6
+ # an already-red target makes every mutation look caught. The Swift suites cannot run here at
7
+ # all -- no Swift toolchain exists on this machine -- so every row below is a TEXT-level check.
8
+ #
9
+ # Rows marked MUST-SURVIVE are controls in the sense of must-survive-controls.tsv, so this
10
+ # manifest exits 1 by design and is not wired into a package.json aggregate script.
11
+ #
12
+ # --- TS side of the cross-language twin. Bound before this task; kept as regression coverage.
13
+ delete src/__tests__/enhancer.test.ts enhances: make this sound more professional src/lib/enhancer.ts professional| 1
14
+ delete src/__tests__/enhancer.test.ts passes through: draft beer is better than bottled in my opinion src/lib/enhancer.ts (?:email|message|response|reply|letter|note|text|slack|dm) 1
15
+ # --- SWIFT regex mirror drift. SURVIVED before enhancement-screen-mirror-contract existed:
16
+ # nothing on Linux read EnhancementScreen.swift, so the 'byte-identical' claim was unenforced.
17
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts the instruction pattern sources are byte-identical in the CLI and the Swift mirror src/native/Recordings/RecordingsLib/EnhancementScreen.swift professional| 1
18
+ # --- and the same drift introduced on the CLI side must break identity too, not just behaviour.
19
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts the instruction pattern sources are byte-identical in the CLI and the Swift mirror src/lib/enhancer.ts professional| 1
20
+ # --- 'shared verbatim fixture table'. SURVIVED before: the tables were the same text only by
21
+ # convention, and the TS fixtures exercised the TS implementation alone.
22
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts the cross-language fixture table is the same text in both suites src/native/Recordings/RecordingsTests/EnhancementScreenTests.swift "draft beer is better than bottled in my opinion" 1
23
+ # --- review F2's fail-closed contract at the PRODUCTION call site. SURVIVED before.
24
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts every Swift call site of shouldPasteBeforePersistence states enhanceTriggersJSON src/native/Recordings/RecordingsLib/RecordingEngine.swift ,\n enhanceTriggersJSON: processingConfiguration.enhanceTriggersJSON\n 1
25
+ # --- and at a test call site: these five were shipped without the argument at all, which does
26
+ # not compile. Swift catches that on macOS; this row makes Linux catch it too.
27
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts every Swift call site of shouldPasteBeforePersistence states enhanceTriggersJSON src/native/Recordings/RecordingsTests/SpeechIntentTests.swift ,\n enhanceTriggersJSON: "[]" 1
28
+ # --- MUST-SURVIVE: a neutral comment deletion in the CLI must not trip the fixture twins.
29
+ delete src/__tests__/enhancer.test.ts enhances: make this sound more professional src/lib/enhancer.ts // Check instruction patterns\n 1
30
+ # --- MUST-SURVIVE: nor may a comment deletion in the Swift mirror trip the identity guard --
31
+ # otherwise the guard is a whole-file checksum rather than an assertion about the patterns.
32
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts the instruction pattern sources are byte-identical in the CLI and the Swift mirror src/native/Recordings/RecordingsLib/EnhancementScreen.swift /// Deterministic local pre-screen for the CLI's enhancement decision.\n 1
33
+
34
+ # --- ADVERSARIAL REVIEW ADDITION (todos 96546cc7). The one place the mirror was LESS
35
+ # conservative than the CLI: `needsEnhancement` tests `lower.includes(trigger.toLowerCase())` and
36
+ # JS `includes("")` is true for every string, so one empty row in enhance_triggers makes the
37
+ # helper rewrite every transcript. Foundation's `contains("")` is FALSE, so the mirror has to
38
+ # special-case the row. It skipped it instead, and deleting the skip SURVIVED all three guards --
39
+ # the fixture twins pin transcript text, never trigger lists. Deleting the fail-closed branch now
40
+ # breaks the new guard's source assertion.
41
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts an empty configured trigger makes both implementations fail closed src/native/Recordings/RecordingsLib/EnhancementScreen.swift guard !loweredTrigger.isEmpty else { return true }\n 1
42
+ # --- and the CLI half of the same claim: deleting the trigger scan makes needsEnhancement stop
43
+ # treating an empty row as a match.
44
+ delete src/__tests__/enhancement-screen-mirror-contract.test.ts an empty configured trigger makes both implementations fail closed src/lib/enhancer.ts if (lower.includes(trigger.toLowerCase())) { 1
@@ -0,0 +1,13 @@
1
+ # The "not TSV-expressible" chain markers, expressed via occurrence index instead of a line delete.
2
+ # A bare identifier's FIRST occurrence is the shell function DEFINITION (`name() {`), which is why a
3
+ # newline-free needle appeared unusable; the CALL site is simply a later occurrence.
4
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh acquire_maintenance_marker 2
5
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh drain_store_reader_leases 2
6
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh acquire_sqlite_barrier 3
7
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh acquire_sqlite_barrier 5
8
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh acquire_sqlite_barrier 6
9
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh stop_old_processes 3
10
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh stop_old_processes 4
11
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh stop_old_processes 5
12
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh trap cleanup_install_coordination_and_work EXIT 1
13
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh write_journal processes-stopped 1
@@ -0,0 +1,10 @@
1
+ # DELIBERATE MUST-SURVIVE CONTROL. `options: .atomic` occurs twice in RecordingsApp.swift and only
2
+ # the SECOND is inside the region under test. Deleting the FIRST (the runtimeSmokeOutput evidence
3
+ # write, outside the region) MUST leave the test green. A battery that reports KILLED here is
4
+ # over-matching, and would send someone to "fix" a guard that is working correctly.
5
+ delete src/__tests__/macos-smoke-process-identity.test.ts uses challenge-bound completion with identity-verified failure cleanup src/native/Recordings/App/RecordingsApp.swift options: .atomic 1
6
+ # Deleting one of FOUR legitimate `run_swift build` invocations must NOT fail: "tests precede any
7
+ # build" is still true with three left. The reorder in reorder.tsv is the mutation that proves that
8
+ # ordering claim is live. A harness that flagged this as a survivor would be over-matching.
9
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins a clean full source revision before native compilation src/native/Recordings/build.sh run_swift build -c "$BUILD_CONFIGURATION" 1
10
+ delete src/__tests__/macos-artifact-binding.test.ts verification and durability gates precede final manifest publication scripts/macos_artifact.ts fsyncSync(descriptor) 1
@@ -0,0 +1,4 @@
1
+ # The semantically meaningful mutation for a 1-of-4 needle: REORDER, not delete. Deleting one of the
2
+ # four legitimate `run_swift build` invocations correctly survives, because "tests precede any build"
3
+ # is still true. Swapping the test past the first build is the mutation the assertion exists for.
4
+ swap src/__tests__/native-release-build-hardening-contract.test.ts pins a clean full source revision before native compilation src/native/Recordings/build.sh run_swift test -c "$BUILD_CONFIGURATION" run_swift build -c "$BUILD_CONFIGURATION"
@@ -0,0 +1,15 @@
1
+ # Source-side mutations for sites whose expectOrder operands are VARIABLES, so the offset-precise
2
+ # test-literal corruption cannot reach them. Occurrence indices are load-bearing.
3
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh SUBMITTED_PKG_SHA256="$(sha256_file "$PKG")" 1
4
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh "$STAGED_VERIFIER" assert-notary-log 1
5
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh --submitted-archive-sha256 "$SUBMITTED_PKG_SHA256" 1
6
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh /usr/bin/xcrun stapler staple 1
7
+ swap src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh --submission-id --submitted-archive-sha256
8
+ delete src/__tests__/macos-smoke-process-identity.test.ts uses challenge-bound completion with identity-verified failure cleanup src/native/Recordings/App/RecordingsApp.swift options: .atomic 2
9
+ duplicate src/__tests__/macos-smoke-process-identity.test.ts uses challenge-bound completion with identity-verified failure cleanup src/native/Recordings/App/RecordingsApp.swift Darwin._exit(0) 1
10
+ delete src/__tests__/macos-smoke-process-identity.test.ts uses challenge-bound completion with identity-verified failure cleanup scripts/smoke_macos_app.sh cleanup() 1
11
+ delete src/__tests__/macos-artifact-command-pinning.test.ts production requirement digests use the Darwin-pinned codesign executable scripts/macos_artifact.ts function assertFilesystemTree( 1
12
+ # The two residual vacuities the semantics reviewer found. Both PASSED before the fix; both must
13
+ # now be KILLED. Occurrence 1 is the authoritative site in each case.
14
+ delete src/__tests__/install-maintenance.test.ts orders exclusivity before recovery and the authoritative stopped-state copy scripts/install_macos_app.sh write_journal processes-stopped 1
15
+ delete src/__tests__/tailscale-cli-resolution.test.ts keeps all test app and tool overrides after the real Darwin branch scripts/resolve_tailscale_cli.sh codesign_executable="${RECORDINGS_TEST_TAILSCALE_CODESIGN_EXECUTABLE:-/usr/bin/codesign}" 1
@@ -0,0 +1,16 @@
1
+ delete src/__tests__/native-release-build-hardening-contract.test.ts normalizes umask-077 staging before nested signing and finalizes data before app signing src/native/Recordings/build.sh normalize_unsigned_app_bundle_modes "$APP_DIR" 1
2
+ delete src/__tests__/native-release-build-hardening-contract.test.ts normalizes umask-077 staging before nested signing and finalizes data before app signing src/native/Recordings/build.sh run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" provenance 1
3
+ delete src/__tests__/native-release-build-hardening-contract.test.ts normalizes umask-077 staging before nested signing and finalizes data before app signing src/native/Recordings/build.sh run_codesign "${APP_SIGN_ARGUMENTS[@]}" 1
4
+ delete src/__tests__/native-release-build-hardening-contract.test.ts normalizes umask-077 staging before nested signing and finalizes data before app signing src/native/Recordings/build.sh run_codesign "${HELPER_SIGN_ARGUMENTS[@]}" 1
5
+ delete src/__tests__/native-release-build-hardening-contract.test.ts normalizes umask-077 staging before nested signing and finalizes data before app signing src/native/Recordings/build.sh run_sensitive_tool "$DITTO_EXECUTABLE" "$APP_DIR" "$OUTPUT_APP_DIR" 1
6
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins a clean full source revision before native compilation src/native/Recordings/build.sh run_swift build -c "$BUILD_CONFIGURATION" 1
7
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins a clean full source revision before native compilation src/native/Recordings/build.sh run_swift test -c "$BUILD_CONFIGURATION" 1
8
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins a validated macOS SDK sysroot before compiling the descriptor guard scripts/build_native_fs_guard.sh "$CLANG" "${COMMON[@]}" -arch arm64 1
9
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins bootstrap path tools before deriving the source tree src/native/Recordings/build.sh HOST_UNAME_EXECUTABLE="/usr/bin/uname" 1
10
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins bootstrap path tools before deriving the source tree src/native/Recordings/build.sh SYSTEM_DIRNAME_EXECUTABLE="/usr/bin/dirname" 1
11
+ delete src/__tests__/native-release-build-hardening-contract.test.ts pins bootstrap path tools before deriving the source tree src/native/Recordings/build.sh SYSTEM_PWD_EXECUTABLE="/bin/pwd" 1
12
+ delete src/__tests__/native-release-build-hardening-contract.test.ts ships a pinned universal descriptor guard and fails closed before installer mutation scripts/install_macos_app.sh "$BUN_EXECUTABLE" "$ARTIFACT_TOOL" native-fs-guard-check 1
13
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh "$STAGED_VERIFIER" assert-notary-log 1
14
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh --submitted-archive-sha256 "$SUBMITTED_PKG_SHA256" 1
15
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh SUBMITTED_PKG_SHA256="$(sha256_file "$PKG")" 1
16
+ delete src/__tests__/release-output-publication-contract.test.ts package notarization also verifies the log digest before stapling packaging/macos/build_release_pkg.sh /usr/bin/xcrun stapler staple 1
@@ -0,0 +1,61 @@
1
+ # Re-derivation battery for the version-site guards (todos 96546cc7), measured on 15303a6.
2
+ #
3
+ # Scope is named explicitly and EXCLUDES the three permanently-red files on this tree
4
+ # (macos-app-lifecycle.test.ts, native-app-companion-contract.test.ts, config.test.ts). An
5
+ # already-red target makes every mutation look caught -- the inverse trap. The driver's
6
+ # RED-ON-CLEAN control enforces that mechanically; the scope is written down so a reader can
7
+ # check it without running anything.
8
+ #
9
+ # Expected verdict for every row below is a kill, EXCEPT the rows explicitly marked
10
+ # MUST-SURVIVE or EXPECTED-SURVIVOR. The driver reports these kills as
11
+ # KILLED-UNATTRIBUTED because these guards assert with `expect` rather than emitting one of the
12
+ # GUARD_SIGNAL messages; attribution was completed by confirming each failure lands on the
13
+ # guard's own assertion line, not on an early throw. GUARD_SIGNAL was deliberately NOT widened
14
+ # to make this file exit 0 -- and, for the same reason must-survive-controls.tsv is not, this
15
+ # manifest is not wired into a package.json aggregate script.
16
+ #
17
+ # --- every version site agrees with package.json. Models 8de7068: one site bumped, the rest not.
18
+ duplicate src/__tests__/version-site-guard.test.ts agree in the working tree, on the version package.json declares src/version.ts .15 1
19
+ duplicate src/__tests__/version-site-guard.test.ts agree in the working tree, on the version package.json declares src/native/Recordings/RecordingsLib/Info.plist 0.2.15 1
20
+ duplicate src/__tests__/version-site-guard.test.ts agree in the working tree, on the version package.json declares src/native/Recordings/RecordingsLib/Info.plist 0.2.15 2
21
+ # --- a stale site is reported, not silently accepted: the mismatch filter removed.
22
+ delete src/__tests__/version-site-guard.test.ts a single stale site is reported rather than silently accepted scripts/set-version.ts .filter((reading) => reading.version !== expected) 1
23
+ # --- applyVersion writes every site in one pass: the per-file grouping removed.
24
+ delete src/__tests__/version-site-guard.test.ts applyVersion rewrites every site in one pass scripts/set-version.ts .filter((candidate) => candidate.file === file) 1
25
+ # --- A DROPPED SITE. The writer silently stops writing a plist key the release path reads, and
26
+ # every site that remains still agrees. Both rows SURVIVED the original assertions
27
+ # (`readings).toHaveLength(versionSites.length)` compares the array to its own source, and
28
+ # `not.toContain("0.0.1")` is satisfied by a key that was never written at all). They are the
29
+ # reason both assertions were re-pointed to independent literal expectations.
30
+ delete src/__tests__/version-site-guard.test.ts applyVersion rewrites every site in one pass scripts/set-version.ts {\n file: "src/native/Recordings/RecordingsLib/Info.plist",\n label: "Info.plist CFBundleVersion",\n pattern: /(<key>CFBundleVersion<\/key>\s*<string>)([^<]+)(<\/string>)/,\n },\n 1
31
+ delete src/__tests__/version-site-guard.test.ts agree in the working tree, on the version package.json declares scripts/set-version.ts {\n file: "src/native/Recordings/RecordingsLib/Info.plist",\n label: "Info.plist CFBundleVersion",\n pattern: /(<key>CFBundleVersion<\/key>\s*<string>)([^<]+)(<\/string>)/,\n },\n 1
32
+ # --- version-format rejection, one anchor at a time. Occurrence 5 of "^" and occurrence 1 of "$"
33
+ # are the two anchors in assertReleaseVersion; the indices are load-bearing. The "$" row SURVIVED
34
+ # until a junk-suffixed case was added, because `v1.2.3` binds only the leading anchor.
35
+ delete src/__tests__/version-site-guard.test.ts rejects versions the release path already refuses scripts/set-version.ts ^ 5
36
+ delete src/__tests__/version-site-guard.test.ts rejects versions the release path already refuses scripts/set-version.ts $ 1
37
+ # --- prepack wiring. Nothing asserted version:check was in prepack at all; the one prepack needle
38
+ # in the repo (native-release-build-hardening-contract's toStartWith) was broken outright by
39
+ # putting version:check ahead of the fail-closed macOS gate.
40
+ delete src/__tests__/version-site-guard.test.ts prepack checks the version sites after the platform gate and before the build package.json bun run version:check && 1
41
+ # --- MUST-SURVIVE CONTROL. A semantically neutral edit to the same file must NOT trip the guard.
42
+ # A battery with no surviving control cannot distinguish a guard from a checksum.
43
+ delete src/__tests__/version-site-guard.test.ts agree in the working tree, on the version package.json declares scripts/set-version.ts /** Label used in check output. */\n 1
44
+
45
+ # --- ADVERSARIAL REVIEW ADDITIONS (todos 96546cc7).
46
+ #
47
+ # The writer's OWN validation call. Deleting it left this whole file at 5 pass / 0 fail while
48
+ # `bun run version:set v9.9.9` exited 0 and wrote `v9.9.9` into all four sites, after which
49
+ # `version:check` reported them in agreement -- nothing else in the repo pins the version's
50
+ # FORMAT. The first row is the guard added to catch it; the two EXPECTED-SURVIVOR rows are the
51
+ # same mutation against the two pre-existing tests, kept because they are the measurement that
52
+ # neither one ever bound the writer.
53
+ delete src/__tests__/version-site-guard.test.ts the writer refuses a version the release path refuses, and writes nothing scripts/set-version.ts assertReleaseVersion(version);\n 1
54
+ # EXPECTED-SURVIVOR
55
+ delete src/__tests__/version-site-guard.test.ts rejects versions the release path already refuses scripts/set-version.ts assertReleaseVersion(version);\n 1
56
+ # EXPECTED-SURVIVOR
57
+ delete src/__tests__/version-site-guard.test.ts applyVersion rewrites every site in one pass scripts/set-version.ts assertReleaseVersion(version);\n 1
58
+ #
59
+ # The generated SDK's version stamp -- a fifth copy of the release version that `versionSites`
60
+ # deliberately does not write -- goes stale again.
61
+ delete src/__tests__/version-site-guard.test.ts the generated SDK header carries the version package.json declares src/sdk/v1.generated.ts .15 1
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Mutation battery for the indexOf/-1 vacuity sweep (OPE41-00029).
3
+ *
4
+ * A converted ordering assertion is only proven non-vacuous by an input that makes it FAIL. This
5
+ * driver takes a manifest of mutations and, for each one:
6
+ *
7
+ * 1. asserts the mutation actually CHANGED the file — a positive control, because a needle that
8
+ * never matched, or an occurrence index past the last match, would otherwise read as a caught
9
+ * mutation when nothing was mutated at all;
10
+ * 2. runs the target test CLEAN and requires it to PASS — without this control an already-red
11
+ * test makes every mutation look caught, which is the inverse trap;
12
+ * 3. applies the mutation and requires the named test to FAIL;
13
+ * 4. restores the file byte-for-byte from the copy held in memory, never from git, so sibling
14
+ * working-tree edits are never reverted.
15
+ *
16
+ * Mutation kinds, and why one is not enough:
17
+ *
18
+ * delete — remove the Nth occurrence of a needle. The occurrence index is not optional.
19
+ * `options: .atomic` occurs twice in RecordingsApp.swift and only the SECOND is
20
+ * inside the region under test, so deleting the first must stay GREEN; a harness
21
+ * that always deletes the first records a false survivor and sends someone to
22
+ * "fix" a guard that was working. Three more needles are masked the same way by an
23
+ * earlier occurrence that is the helper's own DEFINITION rather than its call site.
24
+ * swap — exchange two needles' text so the ordering flips. An ordering assertion that
25
+ * survives a REORDER is the same vacuity class this sweep exists to close wearing
26
+ * different clothes, and it is entirely invisible to a deletion-only harness.
27
+ * duplicate — insert a second copy of a needle. This is what separates a uniqueness bound from
28
+ * a bare `toContain`: the `toContain` stays green on a duplicate, the bound does not.
29
+ * corrupt — replace a string literal in the TEST at an exact byte offset with a sentinel that
30
+ * cannot occur in any source. Equivalent, from the assertion's point of view, to
31
+ * deleting that needle from the source, but it needs no map from haystack variable
32
+ * to source path, so it covers every converted site mechanically.
33
+ *
34
+ * Needles may contain \n and \t as escapes, because several properties can only be pinned by a
35
+ * whole-line needle (a bare helper name matches its own definition first).
36
+ *
37
+ * Exit codes come from spawnSync status, never through a pipe. Runs are strictly serial: a sibling
38
+ * assertion in capture-probe.test.ts scans a shared /tmp, so concurrent suites corrupt each other.
39
+ *
40
+ * Usage: bun scripts/vacuity-mutation-battery.ts <manifest.tsv> [--only <substring>]
41
+ * Manifest, tab-separated: kind <TAB> testFile <TAB> testName <TAB> targetFile <TAB> a <TAB> b
42
+ */
43
+ import { readFileSync, writeFileSync } from "node:fs";
44
+ import { spawnSync } from "node:child_process";
45
+
46
+ type Kind = "delete" | "swap" | "duplicate" | "corrupt";
47
+ type Row = { kind: Kind; testFile: string; testName: string; target: string; a: string; b: string };
48
+
49
+ const [manifestPath, ...rest] = process.argv.slice(2);
50
+ if (!manifestPath) {
51
+ console.error("usage: bun scripts/vacuity-mutation-battery.ts <manifest.tsv> [--only <substr>]");
52
+ process.exit(2);
53
+ }
54
+ const onlyIndex = rest.indexOf("--only");
55
+ const only = onlyIndex >= 0 ? rest[onlyIndex + 1] : undefined;
56
+
57
+ const rows: Row[] = readFileSync(manifestPath, "utf8")
58
+ .split("\n")
59
+ .filter((line) => line.trim() && !line.startsWith("#"))
60
+ .map((line, index) => {
61
+ const [kind, testFile, testName, target, a, b] = line.split("\t");
62
+ // Fail loudly on a malformed row. Redirecting the generator with `2>&1` merges its progress line
63
+ // into the manifest, which parsed as a row with an undefined target and crashed the driver 164
64
+ // rows in — after all the expensive work, and with a stack trace that named the wrong cause.
65
+ if (!kind || !testFile || !testName || !target) {
66
+ console.error(`manifest line ${index + 1} is malformed (${line.split("\t").length} fields): ${line.slice(0, 120)}`);
67
+ process.exit(2);
68
+ }
69
+ return { kind: kind as Kind, testFile, testName, target, a: a ?? "", b: b ?? "" };
70
+ })
71
+ .filter((r) => !only || `${r.testFile} ${r.testName} ${r.a}`.includes(only));
72
+
73
+ /** Byte offset of the nth (1-based) occurrence, or -1. */
74
+ function nthIndex(haystack: string, needle: string, n: number): number {
75
+ let at = -1;
76
+ for (let i = 0; i < n; i++) {
77
+ at = haystack.indexOf(needle, i === 0 ? 0 : at + 1);
78
+ if (at < 0) return -1;
79
+ }
80
+ return at;
81
+ }
82
+
83
+ /** TSV cannot carry literal tabs or newlines, so needles arrive escaped. */
84
+ function unescape(text: string): string {
85
+ return text.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
86
+ }
87
+
88
+ /** Apply a mutation, or return undefined — which the caller treats as a control FAILURE. */
89
+ function mutate(row: Row, original: string): string | undefined {
90
+ const a = unescape(row.a);
91
+ const b = unescape(row.b);
92
+
93
+ if (row.kind === "corrupt") {
94
+ // Deliberately NOT unescaped: this mutates the test file's RAW bytes, and a `\n` inside a
95
+ // TypeScript string literal is two bytes on disk. Unescaping it would move the offset and the
96
+ // guard below would reject every row — silently, as a control failure rather than a survivor.
97
+ const offset = Number(row.a);
98
+ if (!Number.isFinite(offset)) return undefined;
99
+ if (original.slice(offset, offset + row.b.length) !== row.b) return undefined;
100
+ return (
101
+ original.slice(0, offset) +
102
+ row.b +
103
+ "__OPE41_00029_ABSENT__" +
104
+ original.slice(offset + row.b.length)
105
+ );
106
+ }
107
+
108
+ if (row.kind === "delete") {
109
+ const at = nthIndex(original, a, Number(row.b || "1"));
110
+ if (at < 0) return undefined;
111
+ return original.slice(0, at) + original.slice(at + a.length);
112
+ }
113
+
114
+ if (row.kind === "duplicate") {
115
+ const at = nthIndex(original, a, Number(row.b || "1"));
116
+ if (at < 0) return undefined;
117
+ return original.slice(0, at + a.length) + a + original.slice(at + a.length);
118
+ }
119
+
120
+ if (row.kind === "swap") {
121
+ const atA = original.indexOf(a);
122
+ const atB = original.indexOf(b);
123
+ if (atA < 0 || atB < 0 || a === b) return undefined;
124
+ const firstAt = Math.min(atA, atB);
125
+ const secondAt = Math.max(atA, atB);
126
+ const [first, second] = atA < atB ? [a, b] : [b, a];
127
+ if (firstAt + first.length > secondAt) return undefined; // overlapping; cannot swap cleanly
128
+ return (
129
+ original.slice(0, firstAt) +
130
+ second +
131
+ original.slice(firstAt + first.length, secondAt) +
132
+ first +
133
+ original.slice(secondAt + second.length)
134
+ );
135
+ }
136
+ return undefined;
137
+ }
138
+
139
+ /**
140
+ * `bun test -t` takes a REGEX, not a substring.
141
+ *
142
+ * A test named `rendering RecordingEngine.logResolvedTrigger()'s format string parses as expected`
143
+ * contains `()`, which as a pattern is an empty capture group, so the literal parentheses in the
144
+ * name are never matched and bun reports `matched 0 tests` and exits 1. Unescaped, that presented as
145
+ * RED-ON-CLEAN — a refusal to conclude rather than a false pass, but still two sites left unproven
146
+ * for a reason that had nothing to do with them.
147
+ */
148
+ function escapeForTestFilter(name: string): string {
149
+ return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
150
+ }
151
+
152
+ function runTest(testFile: string, testName: string): { code: number; output: string } {
153
+ const filter = escapeForTestFilter(testName);
154
+ const result = spawnSync("bun", ["test", testFile, "-t", filter, "--timeout", "120000"], {
155
+ encoding: "utf8",
156
+ env: { ...process.env, NO_COLOR: "1" },
157
+ maxBuffer: 64 * 1024 * 1024,
158
+ });
159
+ return { code: result.status ?? 1, output: `${result.stdout ?? ""}${result.stderr ?? ""}` };
160
+ }
161
+
162
+ /** Did the filter select anything? 0 pass 0 fail exits 0 and would read as a clean pass. */
163
+ function ranSomething(output: string): boolean {
164
+ const p = output.match(/(\d+)\s+pass/);
165
+ const f = output.match(/(\d+)\s+fail/);
166
+ return (p ? Number(p[1]) : 0) + (f ? Number(f[1]) : 0) > 0;
167
+ }
168
+
169
+ type Verdict =
170
+ | "KILLED"
171
+ | "KILLED-UNATTRIBUTED"
172
+ | "SURVIVED"
173
+ | "MUTATION-NOT-APPLIED"
174
+ | "RED-ON-CLEAN"
175
+ | "FILTER-MATCHED-NOTHING";
176
+
177
+ /**
178
+ * A red test is not automatically proof that the converted assertion fired.
179
+ *
180
+ * Most of these suites EXECUTE the script they also read, so deleting a needle from
181
+ * `install_macos_app.sh` can break the run and fail `expect(result.exitCode).toBe(0)` long before
182
+ * the ordering assertion is reached. The driver would see a non-zero exit and record KILLED while
183
+ * the assertion under test did nothing at all — a kill credited to the wrong cause is exactly the
184
+ * "evidence that does not prove the claim" this sweep exists to remove.
185
+ *
186
+ * So a kill only counts when the failure output carries one of the guard's own messages. Anything
187
+ * else is reported as KILLED-UNATTRIBUTED and proves nothing about the site.
188
+ *
189
+ * The patterns are deliberately fragments rather than whole sentences. Suites add their own named
190
+ * guards — "test override is missing entirely: ...", "resolver function is missing entirely: ..." —
191
+ * and a signal list enumerating only the helper's own wording reported those as UNATTRIBUTED, which
192
+ * cost two hand-attributions that the driver should have made itself.
193
+ */
194
+ const GUARD_SIGNAL =
195
+ /(is missing entirely|marker is missing|marker is not unique|too small to assert anything about|is missing or out of order|no longer sanitizes)/;
196
+
197
+ function attributed(output: string): boolean {
198
+ return GUARD_SIGNAL.test(output);
199
+ }
200
+
201
+ const results: { row: Row; verdict: Verdict }[] = [];
202
+ const cleanCache = new Map<string, "ok" | "red" | "no-match">();
203
+
204
+ for (const [n, row] of rows.entries()) {
205
+ const label = `[${n + 1}/${rows.length}] ${row.kind} ${row.testFile} :: ${row.testName}`;
206
+ const original = readFileSync(row.target, "utf8");
207
+
208
+ const mutated = mutate(row, original);
209
+ if (mutated === undefined || mutated === original) {
210
+ results.push({ row, verdict: "MUTATION-NOT-APPLIED" });
211
+ console.log(`${label}\n MUTATION-NOT-APPLIED ${row.a.slice(0, 70)}`);
212
+ continue;
213
+ }
214
+
215
+ // The cache records WHY a clean run was unusable, not merely that it was. Collapsing both causes
216
+ // into one boolean made every row after the first report RED-ON-CLEAN, so a broken `-t` filter
217
+ // masqueraded as a failing test and pointed the reader at the wrong thing to fix.
218
+ const cleanKey = `${row.testFile} ${row.testName}`;
219
+ if (!cleanCache.has(cleanKey)) {
220
+ const clean = runTest(row.testFile, row.testName);
221
+ cleanCache.set(
222
+ cleanKey,
223
+ !ranSomething(clean.output) ? "no-match" : clean.code === 0 ? "ok" : "red",
224
+ );
225
+ }
226
+ const cleanState = cleanCache.get(cleanKey);
227
+ if (cleanState !== "ok") {
228
+ const verdict: Verdict = cleanState === "no-match" ? "FILTER-MATCHED-NOTHING" : "RED-ON-CLEAN";
229
+ results.push({ row, verdict });
230
+ console.log(`${label}\n ${verdict} - no mutation verdict can be drawn here`);
231
+ continue;
232
+ }
233
+
234
+ try {
235
+ writeFileSync(row.target, mutated, "utf8");
236
+ const after = runTest(row.testFile, row.testName);
237
+ const verdict: Verdict =
238
+ after.code === 0 ? "SURVIVED" : attributed(after.output) ? "KILLED" : "KILLED-UNATTRIBUTED";
239
+ results.push({ row, verdict });
240
+ console.log(`${label}\n ${verdict} (mutated exit ${after.code}) ${row.a.slice(0, 60)}`);
241
+ } finally {
242
+ writeFileSync(row.target, original, "utf8");
243
+ }
244
+ }
245
+
246
+ const tally = results.reduce<Record<string, number>>((acc, r) => {
247
+ acc[r.verdict] = (acc[r.verdict] ?? 0) + 1;
248
+ return acc;
249
+ }, {});
250
+ console.log(`\n==== BATTERY SUMMARY (${results.length} mutations) ====`);
251
+ for (const [verdict, count] of Object.entries(tally).sort()) console.log(`${verdict}: ${count}`);
252
+
253
+ const notKilled = results.filter((r) => r.verdict !== "KILLED");
254
+ if (notKilled.length) {
255
+ console.log(`\n---- NOT KILLED (${notKilled.length}) ----`);
256
+ for (const s of notKilled) {
257
+ console.log(
258
+ `${s.verdict}\t${s.row.kind}\t${s.row.testFile}\t${s.row.testName}\t${s.row.a.slice(0, 60)}`,
259
+ );
260
+ }
261
+ }
262
+
263
+ // An instrument that always exits 0 is not a gate. Anything other than a clean kill — a survivor, a
264
+ // kill credited to the wrong cause, or a control that could not be applied — has to be able to fail
265
+ // a caller, or the summary is decoration that a script above it will happily ignore.
266
+ process.exit(notKilled.length === 0 ? 0 : 1);
@@ -13,8 +13,10 @@ struct MenuBarStatusLabel: View {
13
13
  private var presentation: MenuBarPresentation {
14
14
  MenuBarPresentation(
15
15
  isRecording: store.engine.isRecording,
16
+ isWarmingUpCapture: store.engine.isWarmingUpCapture,
16
17
  canStartRecording: store.engine.canStartRecording,
17
- statusMessage: store.engine.statusMessage
18
+ statusMessage: store.engine.statusMessage,
19
+ blockedReason: store.engine.blockedReason
18
20
  )
19
21
  }
20
22
  }
@@ -43,7 +45,7 @@ struct MenuBarStatusView: View {
43
45
  .frame(maxWidth: .infinity)
44
46
  }
45
47
  .buttonStyle(.borderedProminent)
46
- .tint(store.engine.isRecording ? .red : .accentColor)
48
+ .tint(store.engine.captureIsActive ? .red : .accentColor)
47
49
  .disabled(!presentation.primaryActionEnabled)
48
50
 
49
51
  Divider()
@@ -73,27 +75,32 @@ struct MenuBarStatusView: View {
73
75
  }
74
76
 
75
77
  private var statusColor: Color {
76
- store.engine.isRecording ? .red : .accentColor
78
+ if store.engine.captureIsActive { return .red }
79
+ // The popover repeats the menu-bar signal rather than relying on the caption text, which
80
+ // is rendered `.secondary` and reads as an aside.
81
+ return presentation.isBlocked ? .orange : .accentColor
77
82
  }
78
83
 
79
84
  private var presentation: MenuBarPresentation {
80
85
  MenuBarPresentation(
81
86
  isRecording: store.engine.isRecording,
87
+ isWarmingUpCapture: store.engine.isWarmingUpCapture,
82
88
  canStartRecording: store.engine.canStartRecording,
83
- statusMessage: store.engine.statusMessage
89
+ statusMessage: store.engine.statusMessage,
90
+ blockedReason: store.engine.blockedReason
84
91
  )
85
92
  }
86
93
 
87
94
  private var recordButtonTitle: String {
88
- store.engine.isRecording ? "Stop and Transcribe" : "Start Recording"
95
+ store.engine.captureIsActive ? "Stop and Transcribe" : "Start Recording"
89
96
  }
90
97
 
91
98
  private var recordButtonIcon: String {
92
- store.engine.isRecording ? "stop.fill" : "mic.fill"
99
+ store.engine.captureIsActive ? "stop.fill" : "mic.fill"
93
100
  }
94
101
 
95
102
  private func toggleRecording() {
96
- if store.engine.isRecording {
103
+ if store.engine.captureIsActive {
97
104
  store.engine.stopAndTranscribe()
98
105
  } else {
99
106
  store.engine.startRecording()
@@ -21,8 +21,12 @@ final class RuntimeSmokeProbe: ObservableObject {
21
21
  var presentation: MenuBarPresentation {
22
22
  MenuBarPresentation(
23
23
  isRecording: phase == .recording,
24
+ // The smoke probe drives phases directly and never runs a real recorder, so it has
25
+ // no warm-up window.
26
+ isWarmingUpCapture: false,
24
27
  canStartRecording: phase == .idle,
25
- statusMessage: phase == .transcribing ? "Transcribing" : "Ready"
28
+ statusMessage: phase == .transcribing ? "Transcribing" : "Ready",
29
+ blockedReason: nil
26
30
  )
27
31
  }
28
32
 
@@ -37,7 +37,7 @@ struct SidebarView: View {
37
37
  withAnimation(.spring(response: 0.3, dampingFraction: 0.85)) { store.pane = .record }
38
38
  } label: {
39
39
  rowLabel(icon: "mic.fill", label: "Record", count: nil,
40
- selected: store.pane == .record, accentDot: store.engine.isRecording)
40
+ selected: store.pane == .record, accentDot: store.engine.captureIsActive)
41
41
  }
42
42
  .buttonStyle(.plain)
43
43
  }