---
phase: 13-auto-continue-evaluation
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
autonomous: true
requirements: [EVAL-01, EVAL-02, EVAL-03, EVAL-04, EVAL-06]

must_haves:
  truths:
    - "MiniMax evaluation uses exact legacy SYSTEM_PROMPT verbatim"
    - "Plan files discovered from current transcript AND sibling JSONL files in session directory"
    - "SWEEP_PROMPT matches legacy 5-step pipeline verbatim"
    - "Per-session state persists iteration count, sweep status, and manual intervention across stop-hook invocations"
    - "When all plan checkboxes checked and no review section, returns SWEEP deterministically without MiniMax"
  artifacts:
    - path: "plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift"
      provides: "Full legacy auto-continue evaluation with state tracking"
      contains: "AutoContinueState"
  key_links:
    - from: "AutoContinue.swift"
      to: "MiniMaxClient"
      via: "client.query() with SYSTEM_PROMPT"
      pattern: "SYSTEM_PROMPT"
    - from: "AutoContinue.swift"
      to: "~/.claude/hooks/state/"
      via: "JSON state files per session"
      pattern: "auto-continue-.*\\.json"
---

<objective>
Rewrite AutoContinueEvaluator with the exact legacy evaluation logic: verbatim SYSTEM_PROMPT and SWEEP_PROMPT, per-session state tracking (iteration, sweep_injected, sweep_done, manual intervention detection), sibling JSONL plan discovery, transcript budget building, deterministic sweep fallback, and robust multi-line decision parsing.

Purpose: The Phase 7 stub has a simplified prompt and no state tracking. This plan ports the full legacy TypeScript evaluation engine to Swift so auto-continue decisions match production behavior.

Output: Complete AutoContinue.swift replacing the Phase 7 stub.
</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
@plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/MiniMaxClient.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/TranscriptParser.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/SummaryEngine.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/Config.swift

<interfaces>
<!-- Key types the executor needs -->

From SummaryEngine.swift:

```swift
struct ConversationTurn {
    let prompt: String
    let response: String
    let timestamp: Date?
    let toolSummary: String?
    let toolResults: String?
}
```

From MiniMaxClient.swift:

```swift
struct MiniMaxResult {
    let text: String
    let durationMs: Int
    let outputTokens: Int?
}

final class MiniMaxClient: @unchecked Sendable {
    func query(prompt: String, systemPrompt: String, maxTokens: Int) async throws -> MiniMaxResult
}
```

From TranscriptParser.swift:

```swift
enum TranscriptParser {
    static func parse(filePath path: String) -> [TranscriptEntry]
    static func entriesToTurns(_ entries: [TranscriptEntry]) -> [ConversationTurn]
}
```

From TelegramFormatter.swift:

```swift
enum TelegramFormatter {
    static func escapeHtml(_ text: String) -> String
}
```

</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Rewrite AutoContinue.swift with full legacy evaluation logic</name>
  <files>plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift</files>
  <read_first>
    - plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift (current stub to replace)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/MiniMaxClient.swift (query API)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/TranscriptParser.swift (parse + entriesToTurns)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/SummaryEngine.swift (ConversationTurn type)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/Config.swift (constants)
    - ~/.claude/automation/claude-telegram-sync/src/hooks/auto-continue.ts (legacy source — PORT FROM THIS)
  </read_first>
  <action>
Completely rewrite AutoContinue.swift, replacing the Phase 7 stub. Port the EXACT logic from the legacy TypeScript auto-continue.ts. The file must contain:

**1. ContinueDecision enum** (keep existing):

```swift
enum ContinueDecision: String, Sendable {
    case `continue` = "CONTINUE"
    case sweep = "SWEEP"
    case redirect = "REDIRECT"
    case done = "DONE"
}
```

**2. AutoContinueState struct** (EVAL-04) — port from TypeScript `AutoContinueState` interface:

```swift
struct AutoContinueState: Codable {
    var iteration: Int                // Per-streak counter (resets on manual intervention)
    var totalIterations: Int          // Lifetime counter — never resets
    var sweepInjected: Bool           // Sweep prompt sent
    var sweepDone: Bool               // MiniMax returned DONE after sweep
    var sweepNotified: Bool           // Prevents duplicate "Sweep complete" Telegram spam
    var startedAt: String             // ISO 8601
    var lastBlockedAt: Double?        // Epoch seconds when hook last blocked
}
```

State files stored at `~/.claude/hooks/state/auto-continue-{sessionId}.json`.
Port `loadState()` and `saveState()` exactly — create STATE_DIR if needed, handle corrupted files by resetting.

**3. Constants** — port from TypeScript:

- `MAX_ITERATIONS = 10` (or from env `AUTO_CONTINUE_MAX_ITERATIONS`)
- `MAX_RUNTIME_MIN = 180` (or from env `AUTO_CONTINUE_MAX_RUNTIME_MIN`)
- `TRANSCRIPT_BUDGET = 102400` (or from env `AUTO_CONTINUE_TRANSCRIPT_BUDGET`)
- `AUTO_CONTINUE_WINDOW_MS = 5 * 60 * 1000` (5 min window for auto-continuation detection)
- `ABSOLUTE_MAX = MAX_ITERATIONS * 3` (absolute cap across all streaks)
- `STATE_DIR = ~/.claude/hooks/state/`

**4. SYSTEM_PROMPT** — copy this EXACT text verbatim (EVAL-01):

```
You are an autonomous session evaluator. You receive a session transcript and optionally a plan file. Your job: determine the single best next action.

RESPOND WITH EXACTLY ONE LINE in the format:
DECISION|<your crafted instruction or summary>

Where DECISION is one of:

CONTINUE — Work remains. Your instruction text becomes the user's next message to Claude, so make it specific and actionable. Reference exact deliverables, files, or steps that are unfinished.

SWEEP — Primary work appears done but needs a final multi-agent review pass. Use when the main deliverables are complete but the session hasn't verified quality, updated documentation/memory, or cross-checked against the original request.

REDIRECT — Claude drifted from the original request. Your instruction should re-anchor Claude to what the user actually asked for. Reference the original request and explain what went off track.

DONE — All requested work is complete, or Claude is yielding to the user. Return DONE when the task is finished or when Claude is clearly waiting for user direction.

PRIORITY ORDER (highest to lowest):
  CONTINUE > REDIRECT > SWEEP > DONE

Your job is to maximize Claude's output. The deterministic safety boundaries (max iterations, max runtime) handle the "stop eventually" concern — your job is to find reasons to keep working, not reasons to stop.

MANDATORY DONE SIGNALS (override all other rules — return DONE immediately):
- Claude asks the user what to do next ("What would you like to work on?", "Is there anything else?", "Want me to continue into Phase X?", "Shall I proceed?")
- Claude presents options and waits for user choice
- Claude says the task is complete and offers to help with something new
- The last assistant message is a question directed at the user requesting input or a decision
These patterns mean Claude has YIELDED CONTROL to the user. Continuing would bypass the user's agency. ALWAYS return DONE for these patterns, even if you think more work could be done — the user will decide.

INSTRUCTION TEXT RULES:
- Your instruction text becomes the user's next message to Claude verbatim
- It MUST be a direct, imperative instruction (e.g., "Update the memory file with the gap-fill results")
- NEVER output raw commands, file paths, code snippets, or shell commands as the instruction
- NEVER extract or echo content from the transcript as your instruction
- BAD: "tail -20 /tmp/orchestrator.log" — this is a command, not an instruction
- GOOD: "Check the orchestrator logs on bigblack to verify it's running correctly"

EVALUATION RULES:
1. Read the ENTIRE transcript to understand what was requested and what was delivered.
2. If a plan file is provided, use it as the authority for what needs to be done.
3. If no plan file exists (marked "NO_PLAN"), infer deliverables from the user's messages in the transcript. Look for numbered lists, checkboxes, "Output:" sections, multi-step prompts, or explicit requests.
4. For multi-deliverable prompts (e.g., "Output: updated plan, updated memory, completed deliverables"), check EACH deliverable individually. If any is missing → CONTINUE.
5. ACTIVELY LOOK for reasons to continue. Check for: incomplete deliverables, code that lacks tests, missing documentation updates, memory files that should be updated, GitHub issues that could be commented on or closed, error handling gaps, edge cases, opportunities to improve code quality.
6. Even if the primary task appears done, look for adjacent value: Did Claude update project memory? Did Claude commit the changes? Are there GitHub issues to update? Could the solution be more robust?
7. SWEEP when coding work is done but quality verification, documentation, or cross-checking hasn't happened yet.
8. REDIRECT when the last few turns show Claude working on something unrelated to the original request.
9. DONE when Claude is asking the user a question, presenting choices, or explicitly yielding control. Do NOT continue past a yield point.
10. Your instruction text is critical — Claude will receive it verbatim as a user message. Write it as a direct, imperative instruction — never a raw command or code snippet.
```

**5. SWEEP_PROMPT** — copy this EXACT text verbatim (EVAL-03):

```
Execute this 5-step sweep pipeline. Each step feeds context into the next — run them in order.

## Step 1: Blind Spot Analysis (diagnostic foundation)
Run /devops-tools:session-blind-spots to get a 50-perspective MiniMax consensus analysis of this session. This surfaces what we missed, overlooked, or got wrong — security gaps, untested changes, stale docs, silent failures, architectural issues. Save the ranked findings — every subsequent step should cross-reference them.

## Step 2: Plan Audit + Gap Identification (uses Step 1)
Review the plan file against what was actually delivered in this session. Cross-reference the blind spot findings from Step 1 to distinguish real gaps from noise. Read our project memory files and relevant GitHub Issues. For each plan item, classify as: ✅ done, ⚠️ partially done (specify what's missing), or ❌ not started. Also identify implicit deliverables the user likely expected but didn't explicitly list (e.g., commits, memory updates, issue hygiene).

## Step 3: FOSS Discovery (uses Step 2 gaps)
For each gap or hand-rolled solution identified in Step 2, search ~/fork-tools and the internet for SOTA well-maintained FOSS that could replace or improve it. Fork (not clone) promising projects to ~/fork-tools and deep-dive them. Adopt lightweight ideations from heavy FOSS rather than importing wholesale. Be expansive — I don't mind scope creeps, but keep changes aligned with the plan's goals.

## Step 4: Execute Remaining Work (uses Steps 2 + 3)
Fix gaps identified in Step 2 using FOSS insights from Step 3 where applicable. Complete partially-done deliverables. For gaps that can't be resolved now, document them clearly. Be thorough — finish what's necessary.

## Step 5: Reconcile + Summarize (uses all above)
- Update the plan file to reflect current state
- Update project memory with session learnings
- GitHub Issues: close completed issues with evidence, update in-progress issues, file new issues for deferred gaps from Step 2
- Output: a concise list of what you changed and why, blind spot findings that were actionable, and any deferred items with their new issue numbers
```

**6. Plan discovery** (EVAL-02) — port `discoverPlanFromTranscript()`:

- Scan current transcript for `.claude/plans/{name}.md` references using regex
- For each match, check if the file exists at `~/.claude/plans/{name}`, return first existing
- Fallback: scan sibling JSONL files in the same directory as the transcript, sorted most-recent-first by mtime
  - For each sibling, scan for plan references, prefer main plans (no "-agent-" in filename) over sub-plans
  - Check sibling's state file: if sweep_done=true, return nil (plan is finished, don't dig deeper)
  - If sweep_done=false or no state, return the plan path (adopt it)
- Return nil if no plan found

**7. Transcript building** — port `buildTranscriptText()`:

- Budget constants: maxPromptChars=2000, maxResponseChars=4000, maxToolResultChars=1500
- Format each turn as `=== Turn {i+1} ===\nUser request:\n{prompt}\n\nOutcome:\n{response}{tools}{results}`
- Accumulate turns up to TRANSCRIPT_BUDGET chars, then append `[remaining turns omitted for length]`

**8. Evaluation function** — port `evaluateCompletion()`:

- Build prompt with optional plan section (truncate plan to 15000 chars)
- If no plan, include "NO PLAN FILE" section instructing MiniMax to infer deliverables
- If sweep in progress (sweep_injected && !sweep_done), include SWEEP IN PROGRESS section
- Call `client.query(prompt: ..., systemPrompt: SYSTEM_PROMPT, maxTokens: 2048)`

**9. Decision parsing** — port `parseDecision()` exactly:

- Multi-line aware: scan lines, for each line check if it starts with a decision keyword (CONT, SWEEP, REDIR, DONE prefix match)
- Handle pipe-delimited format: `DECISION|reason text`
- Remaining lines after decision line are appended to reason
- Indirect match: check each pipe-delimited field for decision keyword
- Default to DONE on empty or unparseable response (fail-open)

**10. Deterministic sweep detection** (EVAL-06) — port `detectSweepNeeded()`:

- If no plan or plan is "NO_PLAN", return false
- If plan has `[x]` checked items AND no `[ ]` unchecked items:
  - Check if plan has a review/sweep section (regex: `##\s*(final review|sweep|review|post-implementation)`)
  - If no review section, return true (needs sweep)
- If plan has no checkboxes at all, return true (non-checkbox plans always sweep on first DONE)
- If plan still has unchecked items, return false

**11. Main evaluate() method** — port the `main()` function logic:
The `evaluate()` method signature changes to accept the full hook input and return a structured result:

```swift
struct EvaluationResult {
    let decision: ContinueDecision
    let reason: String
    let shouldBlock: Bool          // true = blockStop, false = allowStop
    let blockReason: String?       // The text to inject (reason for CONTINUE/REDIRECT, SWEEP_PROMPT for SWEEP)
    let state: AutoContinueState   // Current state after evaluation
    let planPath: String?
    let planContent: String?
    let turnCount: Int
    let toolCalls: Int
    let toolBreakdown: String
    let errors: Int
    let gitBranch: String?
    let elapsedMin: Double
}
```

The evaluation flow must match TypeScript exactly:

1. Load state, detect auto-continuation vs manual intervention (5-min window on lastBlockedAt)
2. On manual intervention: reset iteration to 0 (but preserve totalIterations, sweepInjected, sweepDone)
3. Check absolute cap (MAX_ITERATIONS \* 3 on totalIterations)
4. Check per-streak limit (MAX_ITERATIONS on iteration)
5. Check max runtime
6. Check sweep_done — if true, allow stop (send "Sweep complete" once via sweepNotified flag)
7. Parse transcript, discover plan, build transcript text
8. Handle 0-turn session with un-swept sibling plan (inject sweep directly)
9. Call MiniMax evaluation
10. Apply deterministic sweep fallback if MiniMax says DONE but checkboxes are all checked
11. Update state and return result

**12. Tool breakdown** — port the aggregation logic:

- Aggregate tool counts across all turns from toolSummary strings
- Exclude subagent orchestration tools: Agent, Task, TaskCreate, TaskGet, TaskList, TaskOutput, TaskUpdate, TaskStop
- Sort by count descending, take top 6
- Format as "Bash61 Edit54 Read55" (name + count, space-separated)

**13. Checkbox progress helpers** — port `checkboxCounts()` and `progressBar()`:

- `checkboxCounts()`: count `[x]` and `[ ]` patterns in plan content
- `progressBar()`: filled blocks + empty blocks, width=10
- `extractPlanTitle()`: first `# Title` line from plan content
  </action>
  <verify>
  <automated>cd /Users/terryli/eon/cc-skills && swift build 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
  - AutoContinue.swift contains SYSTEM_PROMPT matching legacy verbatim (EVAL-01)
  - AutoContinue.swift contains SWEEP_PROMPT matching legacy verbatim (EVAL-03)
  - AutoContinueState struct with iteration, totalIterations, sweepInjected, sweepDone, sweepNotified, startedAt, lastBlockedAt (EVAL-04)
  - loadState/saveState persists to ~/.claude/hooks/state/ as JSON (EVAL-04)
  - discoverPlanFromTranscript scans current transcript + sibling JSONLs (EVAL-02)
  - detectSweepNeeded returns true when all checkboxes checked with no review section (EVAL-06)
  - Manual intervention detection resets iteration but preserves sweep state (EVAL-04)
  - `swift build` succeeds with zero errors
    </acceptance_criteria>
    <done>AutoContinue.swift fully rewritten with all 13 components above, swift build passes</done>
    </task>

</tasks>

<verification>
```bash
cd /Users/terryli/eon/cc-skills && swift build 2>&1 | tail -5
# Verify SYSTEM_PROMPT is verbatim
grep -c "autonomous session evaluator" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
# Verify SWEEP_PROMPT is verbatim
grep -c "Blind Spot Analysis" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
# Verify state tracking
grep -c "AutoContinueState" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
# Verify sibling discovery
grep -c "sibling" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
# Verify deterministic sweep
grep -c "detectSweepNeeded" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
```
</verification>

<success_criteria>

1. SYSTEM_PROMPT in AutoContinue.swift matches legacy TypeScript verbatim (EVAL-01)
2. Plan discovery scans transcript + sibling JSONLs with sweep_done check (EVAL-02)
3. SWEEP_PROMPT matches legacy 5-step pipeline verbatim (EVAL-03)
4. AutoContinueState persists iteration, sweep status, manual intervention detection to JSON files (EVAL-04)
5. detectSweepNeeded returns SWEEP when all checkboxes done with no review section (EVAL-06)
6. `swift build` succeeds with zero errors
   </success_criteria>

<output>
After completion, create `.planning/phases/13-auto-continue-evaluation/13-01-SUMMARY.md`
</output>
