Branch under test: verify/backfill-2026-06-30 (tip = main = f5282a0)
Pre-cleanup baseline: main~1 (cdc6585)
Date: 2026-06-30
Verdict: EQUIVALENT — no behavior divergence found
| Branch | Commit | Tests run | Passed | Failed |
|---|---|---|---|---|
pre-cleanup (main~1) |
cdc6585 |
5 | 5 | 0 |
cleaned (main) |
f5282a0 |
5 | 5 | 0 |
Both suites ran via git worktree add ../livingtts-pre main~1 + swift test in each tree.
Same 5 tests, same results — full parity confirmed.
New file: Tests/LivingTTSTests/CleanupCharacterizationTests.swift (10 tests, all passing).
Characterization: NSWindow(contentRect: NSRect(0,0,760,560), ...) already establishes
content size 760×560. The subsequent setContentSize call in main~1 was a no-op.
A/B status: Automated (both branches can run the same tests).
Tests:
testWindowContentSizeIsSetByContentRect— creates NSWindow with the samecontentRect:parameters as the app and assertscontentRect(forFrameRect:)returns 760×560. Passes.testRedundantSetContentSizeIsNoOp— additionally callssetContentSize(760,560)and asserts the content rect is still 760×560. Passes.
Why not testing LivingTTSApp.controlPanelWindow directly: LivingTTSApp is @main
(executable entry point) and controlPanelWindow is private. The struct cannot be
instantiated from the test target. The AppKit invariant is tested in isolation with the
same init parameters used in app code.
Characterization: The set contents are identical in both revisions:
{ava, allison, samantha, tom, daniel, serena, jamie, zoe} (8 names, lowercase).
Changing to static let means the set is allocated once at class load time rather than
reallocated on every voiceRank(_:) call. The membership — and therefore voice sort order —
is identical.
A/B status: Automated (behavioral) + Reasoned-from-source (set contents).
Test:
testSystemTTSBackendAvailableVoicesIsCallable— callsSystemTTSBackend().availableVoices()(the public API that exercisespreferredEnglishVoiceNamesinternally) and asserts all returnedVoiceOptionvalues have non-emptyidanddisplayName. Passes.
Why direct set comparison is not possible: preferredEnglishVoiceNames is private in
both revisions (Swift access control; @testable import exposes internal but not private).
Set-content equivalence is confirmed by source-level inspection of both main~1 and f5282a0.
Characterization: Both MenuBarMenuView and MiniPlayerPanelView in main~1 (cdc6585)
contained identical inline badgeText and badgePalette computed vars. The cleaned version
replaces both with calls to the new ControlPanelTheme.badgeDescriptor(for:) static helper.
Pre-cleanup inline mapping (source-verified at cdc6585):
| PlaybackState | text | fill | foreground |
|---|---|---|---|
.idle |
"Ready" | accentGreenFill |
accentGreenText |
.capturing / .cleaning / .preparingSpeech |
"Working" | accentYellowFill |
accentYellowText |
.speaking |
"Reading" | accentBlueFill |
accentBlueText |
.paused |
"Paused" | accentBlueFill |
accentBlueText |
.error(_) |
"Needs Attention" | accentRedFill |
accentRedText |
A/B status: Automated on cleaned branch (badgeDescriptor is new in this commit and only
exists post-cleanup). Pre-cleanup comparison: reasoned-from-pre-cleanup-source (inline
logic read directly from main~1 source; both view files agreed on all 5 cases).
Tests (all on cleaned branch, all passing):
testBadgeDescriptorIdle—.idle→ ("Ready", green, green) ✓testBadgeDescriptorCapturing—.capturing→ ("Working", yellow, yellow) ✓testBadgeDescriptorCleaning—.cleaning→ ("Working", yellow, yellow) ✓testBadgeDescriptorPreparingSpeech—.preparingSpeech→ ("Working", yellow, yellow) ✓testBadgeDescriptorSpeaking—.speaking→ ("Reading", blue, blue) ✓testBadgeDescriptorPaused—.paused→ ("Paused", blue, blue) ✓testBadgeDescriptorError—.error("any message")→ ("Needs Attention", red, red) ✓
| Step | Result |
|---|---|
swift build |
Build complete (0.1s), exit 0 |
swift run (8s timeout) |
No crash output, no console errors; process killed by timeout (exit 124) — expected for a persistent menu-bar GUI app |
Note on headless menu-bar observation: LivingTTS is an NSApplication-based menu-bar extra.
Visual confirmation (menu-bar icon appearance, floating panel rendering) requires an interactive
display session. Launch success + absence of console errors is the observable signal in a
headless environment.
| Refactor | Tests | A/B Method | Result |
|---|---|---|---|
| REDUNDANT-01 (window size) | 2 new tests | Automated (AppKit invariant) | EQUIVALENT |
| COMPLEX-02 (voice names set) | 1 new test | Automated (behavioral) + Reasoned-from-source (set contents) | EQUIVALENT |
| DUP-01 (badge descriptor) | 7 new tests | Automated on cleaned; Reasoned-from-source for pre-cleanup | EQUIVALENT |
| Full suite parity | 5 existing | Automated A/B (both worktrees) | PARITY — 5/5 on both |
| Smoke build | — | swift build exit 0 |
PASS |
| Smoke launch | — | swift run 8s no errors |
PASS |
Verdict: EQUIVALENT. All three refactors are behavior-preserving. No divergence found.