# Handoff 05: Conversation ownership (auto vs human) — stop auto-sending once Quinn takes over

_For a coder agent in `@mac-sync`. Created 2026-06-28. Companion to H03 (outbox) and H04 (mark-read). Makes "who owns this thread" first-class so the auto-handler/outbox never texts a lead Quinn has personally taken over._

## Why
Quinn and the auto-handler send from the **same iMessage account**, so `chat.db` can't distinguish her sends from the bot's (both `is_from_me=1`). Today this is tracked in local Executor files (`state/human-owned.json` + `state/claude-sent.jsonl`) — a stopgap. It belongs in macsync so the outbox enforces it server-side and any client can read it.

## Goal
A per-conversation **owner** state — `auto` (bot may send) vs `human` (Quinn took over; bot must NOT send) — that the outbox honors, plus reliable **takeover detection**.

## Subtasks
1. **Owner field.** Add `owner` ('auto'|'human') + `owned_at` + `owned_reason` to the conversation-read entity (or a sibling `conversation_owner` table), keyed `(device_id, handle)`. Default `auto`.
2. **Sent-ledger.** macsync already records outbox sends with true status — treat every outbox-originated send as "bot-sent." Persist enough (recipient + time + body hash) to compare against observed outbound.
3. **Takeover detection (the core).** On message ingest, for each outbound observed in `chat.db` that does NOT match a bot-sent ledger row → it was **typed by Quinn** → set that conversation `owner='human'` (`reason='manual_send'`). Also expose an explicit `POST /my/own` to set/clear owner (escalation, or hand back to auto).
4. **Enforce in the outbox.** The outbox drain MUST skip/seed-fail any row whose conversation is `owner='human'` (alongside the existing vendor/do-not-text guard). Never send to a human-owned thread.
5. **Expose.** `GET /my/own/:handle` + include owner in the read/state surfaces and MCP tools so triage/prospector/app can check before drafting.
6. **Tests.** Takeover detection (bot-sent vs human-sent), outbox skip on human-owned, set/clear. `make build`/`make test` green.

## Constraints (from `@mac-sync/CLAUDE.md`)
NEVER commit · NEVER `pkill node` · complete production code only (STOP→REPORT→WAIT if blocked) · reuse `shared/db` singleton · no writes to Apple's `chat.db`.

## Acceptance
- A conversation flips to `owner='human'` automatically when Quinn sends a manual message (not matched in the bot ledger), and on escalation.
- The outbox refuses to send to `owner='human'` conversations.
- Owner state is readable via surface + MCP; can be set/cleared.
- Tests green.

## Hardening note (from 2026-06-28 false-positive incident)
Takeover detection produced **7 false positives** because it compared `chat.db` outbound against a sent-ledger that didn't yet cover historical/bot sends — the bot's own unlogged SMS-recoveries were misread as "Quinn typed it." Therefore: (1) use a **ledger watermark** — only infer human-ownership from outbound dated AFTER reliable send-logging began; never from pre-watermark history. (2) **Log every bot-originated send, including SMS recoveries**, so they're never mistaken for manual. (3) When unsure, do NOT flag — the primary ownership signals are escalation + an explicit takeover; the ledger-diff is best-effort secondary.

## Out of scope
The triage/prospector decision of WHEN to escalate (Executor + prospector handoff); the local Executor stopgap files (superseded once this ships).
