---
phase: 28-memory-lifecycle-cleanup
plan: 01
type: execute
wave: 2
depends_on: ["27-01"]
files_modified:
  - plugins/claude-tts-companion/Sources/CompanionCore/MemoryLifecycle.swift
  - plugins/claude-tts-companion/Sources/CompanionCore/TTSEngine.swift
  - plugins/claude-tts-companion/Sources/CompanionCore/CompanionApp.swift
  - plugins/claude-tts-companion/Sources/CompanionCore/TelegramBot.swift
  - plugins/claude-tts-companion/Sources/CompanionCore/HTTPControlServer.swift
autonomous: true
requirements: [MEM-01, MEM-02, MEM-03]

must_haves:
  truths:
    - "No synthesis-count restart logic exists in TTSEngine"
    - "No exit(42) call exists anywhere in CompanionCore"
    - "No checkMemoryLifecycleRestart() calls exist in TelegramBot or HTTPControlServer"
    - "MemoryLifecycle.swift is deleted"
    - "swift build -c release succeeds"
    - "Health endpoint no longer reports tts_restart_threshold or mlx_memory fields"
  artifacts:
    - path: "plugins/claude-tts-companion/Sources/CompanionCore/TTSEngine.swift"
      provides: "Clean TTSEngine without synthesis counter or restart logic"
    - path: "plugins/claude-tts-companion/Sources/CompanionCore/CompanionApp.swift"
      provides: "CompanionApp without plannedRestart() or exit(42)"
    - path: "plugins/claude-tts-companion/Sources/CompanionCore/HTTPControlServer.swift"
      provides: "Simplified health endpoint without MLX memory fields"
  key_links:
    - from: "TelegramBot.swift"
      to: "pipelineCoordinator.startBatchPipeline"
      via: "onComplete callback"
      pattern: "onComplete.*isStreamingInProgress"
    - from: "HTTPControlServer.swift"
      to: "healthResponse()"
      via: "GET /health route"
      pattern: "status.*ok.*uptime_seconds.*rss_mb"
---

<objective>
Remove all IOAccelerator leak mitigation code from the Swift companion.

Purpose: With MLX removed (Phase 27), no Metal GPU memory leak exists in the Swift process. The synthesis-count restart, exit(42), MemoryLifecycle module, and checkMemoryLifecycleRestart() are all dead code that adds complexity and causes unnecessary 2-second service interruptions. Removing them makes the service simpler and continuously available.

Output: Clean CompanionCore with no restart logic, simplified health endpoint, deleted MemoryLifecycle.swift, passing build.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/27-mlx-dependency-removal/27-01-SUMMARY.md

<interfaces>
<!-- Key code locations the executor must modify. Extracted from codebase grep. -->

From TTSEngine.swift — REMOVE these members:

```swift
private(set) var synthesisCount: Int = 0                          // line 73
static let maxSynthesisBeforeRestart = 500                        // line 79
var shouldRestartForMemory: Bool { synthesisCount >= ... }         // line 84-86
func memoryDiagnostics() -> (synthesisCount: Int, mlxActive: Int?, mlxCache: Int?, mlxPeak: Int?) // line 89-91
synthesisCount += 1  // appears at lines 166, 196, 309, 344 (4 increments across 4 synthesis paths)
```

From CompanionApp.swift — REMOVE:

```swift
// line 67-69: MemoryLifecycle.register(ttsEngine: ttsEngine) { ... plannedRestart(...) }
// line 281-296: func plannedRestart(reason:) — hides panel, stops watchers/bot, exit(42)
// line 302-307: func checkMemoryLifecycleRestart() async — checks shouldRestartForMemory
```

From TelegramBot.swift — REMOVE:

```swift
// line 310: Task { await checkMemoryLifecycleRestart() }  // inside startBatchPipeline onComplete
```

From HTTPControlServer.swift — REMOVE:

```swift
// line 248: Task { await checkMemoryLifecycleRestart() }  // inside TTS test onComplete
// line 294: let diag = await ttsEngine.memoryDiagnostics()
// line 300-301: tts_synthesis_count/tts_restart_threshold in HealthResponse construction
// line 302-304: mlx_memory_active_mb/cache_mb/peak_mb in HealthResponse construction
```

From HealthResponse struct (HTTPControlServer.swift lines 44-54) — REMOVE fields:

```swift
let tts_synthesis_count: Int
let tts_restart_threshold: Int
let mlx_memory_active_mb: Int?
let mlx_memory_cache_mb: Int?
let mlx_memory_peak_mb: Int?
```

From MemoryLifecycle.swift (42 lines) — DELETE entire file.
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Delete MemoryLifecycle.swift and strip restart logic from TTSEngine, CompanionApp, TelegramBot, HTTPControlServer</name>
  <files>
    plugins/claude-tts-companion/Sources/CompanionCore/MemoryLifecycle.swift
    plugins/claude-tts-companion/Sources/CompanionCore/TTSEngine.swift
    plugins/claude-tts-companion/Sources/CompanionCore/CompanionApp.swift
    plugins/claude-tts-companion/Sources/CompanionCore/TelegramBot.swift
    plugins/claude-tts-companion/Sources/CompanionCore/HTTPControlServer.swift
  </files>
  <read_first>
    plugins/claude-tts-companion/Sources/CompanionCore/MemoryLifecycle.swift
    plugins/claude-tts-companion/Sources/CompanionCore/TTSEngine.swift
    plugins/claude-tts-companion/Sources/CompanionCore/CompanionApp.swift
    plugins/claude-tts-companion/Sources/CompanionCore/TelegramBot.swift
    plugins/claude-tts-companion/Sources/CompanionCore/HTTPControlServer.swift
  </read_first>
  <action>
**MemoryLifecycle.swift (per MEM-02):**

1. DELETE the entire file. It is 42 lines of dead code — the `MemoryLifecycle` enum, the `checkMemoryLifecycleRestart()` free function, and the logger.

**TTSEngine.swift (per MEM-01):**

1. Remove `private(set) var synthesisCount: Int = 0`
2. Remove `static let maxSynthesisBeforeRestart = 500`
3. Remove `var shouldRestartForMemory: Bool` computed property
4. Remove `func memoryDiagnostics()` function
5. Remove ALL `synthesisCount += 1` lines (4 occurrences across synthesize, synthesizeWithTimestamps, streaming, and CJK paths)
6. Update log messages that reference `count=\(synthesisCount)` — either remove the count from the log or replace with a simpler message. The log should still report audio duration and word onset count where applicable.
7. Keep all other TTSEngine functionality intact — HTTP client, WAV utilities, circuit breaker, CJK routing.

**CompanionApp.swift:**

1. Remove the `MemoryLifecycle.register(...)` block (3 lines around line 67-69)
2. Remove the entire `plannedRestart(reason:)` method (lines 281-296) — this contains exit(42)
3. Remove the entire `checkMemoryLifecycleRestart()` method (lines 302-307)
4. Remove the comment about memory lifecycle registration
5. Keep all other CompanionApp functionality — start(), shutdown(), wiring.

**TelegramBot.swift:**

1. Remove `Task { await checkMemoryLifecycleRestart() }` from the `startBatchPipeline` onComplete callback (line 310). Keep the rest of the callback intact (the `isStreamingInProgress = false` and logging).

**HTTPControlServer.swift:**

1. Remove `Task { await checkMemoryLifecycleRestart() }` from the TTS test onComplete callback (line 248). Keep the rest of the callback.
2. Simplify the `healthResponse()` method:
   - Remove `let diag = await ttsEngine.memoryDiagnostics()`
   - Remove `tts_synthesis_count`, `tts_restart_threshold`, `mlx_memory_active_mb`, `mlx_memory_cache_mb`, `mlx_memory_peak_mb` from the HealthResponse construction
3. Simplify the `HealthResponse` struct:
   - Remove the 5 fields: `tts_synthesis_count`, `tts_restart_threshold`, `mlx_memory_active_mb`, `mlx_memory_cache_mb`, `mlx_memory_peak_mb`
   - Keep: `status`, `uptime_seconds`, `rss_mb`, `subsystems`
   - This is a breaking API change for /health consumers (SwiftBar) but acceptable since those fields were MLX-specific and now meaningless
     </action>

  <verify>
    <automated>cd plugins/claude-tts-companion && swift build -c release 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - `ls plugins/claude-tts-companion/Sources/CompanionCore/MemoryLifecycle.swift 2>/dev/null` returns nothing (file deleted)
    - `grep -rc 'synthesisCount\|maxSynthesisBeforeRestart\|shouldRestartForMemory\|memoryDiagnostics' plugins/claude-tts-companion/Sources/` returns 0
    - `grep -rc 'exit(42)\|plannedRestart\|checkMemoryLifecycleRestart\|MemoryLifecycle' plugins/claude-tts-companion/Sources/` returns 0
    - `grep -c 'mlx_memory\|tts_restart_threshold' plugins/claude-tts-companion/Sources/CompanionCore/HTTPControlServer.swift` returns 0
    - `swift build -c release` exits 0
  </acceptance_criteria>
  <done>MemoryLifecycle.swift deleted; no synthesis counter, restart logic, exit(42), or MLX memory fields remain in any source file; release build succeeds</done>
</task>

<task type="auto">
  <name>Task 2: Run tests and verify clean build with no dead code references</name>
  <files>
    plugins/claude-tts-companion/Tests/CompanionCoreTests/
  </files>
  <read_first>
    plugins/claude-tts-companion/Tests/CompanionCoreTests/
  </read_first>
  <action>
**Test verification:**

1. Run `cd plugins/claude-tts-companion && swift test` — all tests must pass. No test should reference MemoryLifecycle, synthesisCount, or shouldRestartForMemory.

2. If any test references removed APIs, update or remove it:
   - `grep -r 'MemoryLifecycle\|synthesisCount\|shouldRestartForMemory\|memoryDiagnostics\|checkMemoryLifecycleRestart' Tests/`
   - Fix any matches.

**Full verification sweep:**

1. Run a comprehensive dead-code grep across the entire Sources/ and Tests/ to confirm zero references remain:

   ```
   grep -rn 'MemoryLifecycle\|synthesisCount\|maxSynthesisBeforeRestart\|shouldRestartForMemory\|memoryDiagnostics\|checkMemoryLifecycleRestart\|plannedRestart\|exit(42)\|tts_restart_threshold\|mlx_memory' plugins/claude-tts-companion/Sources/ plugins/claude-tts-companion/Tests/
   ```

   Must return empty.

2. Verify health endpoint JSON shape is valid by checking the HealthResponse struct has only the kept fields (status, uptime_seconds, rss_mb, subsystems).

**Per MEM-03 (RSS verification note):**
The RSS-under-100MB criterion requires runtime verification with 50+ TTS calls routed through the Python server. This cannot be verified at build time. Document in the SUMMARY that runtime verification should be done post-deployment by monitoring stderr.log RSS reports over 50+ synthesis calls.
</action>
<verify>
<automated>cd plugins/claude-tts-companion && swift test 2>&1 | tail -10 && echo "---DEAD-CODE-CHECK---" && grep -rn 'MemoryLifecycle\|synthesisCount\|maxSynthesisBeforeRestart\|shouldRestartForMemory\|exit(42)' Sources/ Tests/ ; echo "EXIT:$?"</automated>
</verify>
<acceptance_criteria> - `swift test` exits 0 with all tests passing - `grep -rn 'MemoryLifecycle\|synthesisCount\|shouldRestartForMemory\|exit(42)' plugins/claude-tts-companion/Sources/ plugins/claude-tts-companion/Tests/` returns empty - No compilation warnings related to unused code in the removed areas
</acceptance_criteria>
<done>All tests pass; zero dead-code references remain; MEM-03 runtime verification documented for post-deployment</done>
</task>

</tasks>

<verification>
1. `ls plugins/claude-tts-companion/Sources/CompanionCore/MemoryLifecycle.swift` fails (file deleted)
2. `grep -r 'exit(42)\|plannedRestart\|checkMemoryLifecycleRestart\|MemoryLifecycle\|synthesisCount\|shouldRestartForMemory\|memoryDiagnostics\|tts_restart_threshold\|mlx_memory' plugins/claude-tts-companion/Sources/ plugins/claude-tts-companion/Tests/` returns zero matches
3. `cd plugins/claude-tts-companion && swift build -c release` succeeds
4. `cd plugins/claude-tts-companion && swift test` passes
</verification>

<success_criteria>

- MEM-01: No synthesisCount, maxSynthesisBeforeRestart, shouldRestartForMemory, or exit(42) in TTSEngine or CompanionApp
- MEM-02: MemoryLifecycle.swift deleted; no checkMemoryLifecycleRestart() calls anywhere
- MEM-03: Runtime RSS verification documented for post-deployment (build-time verification confirms no MLX code paths remain)
  </success_criteria>

<output>
After completion, create `.planning/phases/28-memory-lifecycle-cleanup/28-01-SUMMARY.md`
</output>
