# Email-Reactor fail-closed fix — classifier failure / partner mail no longer auto-archived (MC #103815)

## Incident / Root Cause

`~/system/daemons/email-agent.js` was **FAIL-OPEN**. When Ollama classification failed (request timeout, JSON parse error, or no-JSON-match), `ollamaClassify` resolved to `{category:'INFO', priority:'low'}`. The auto-archive block then archived any info/spam/own row. The strategic-partner elevation block only ran when `dbCategory === 'ACTION'`, so a misclassified partner email was never elevated.

**Net effect:** A revenue email from strategic partner **Asmir Merdžanović** ("QODY" project, email #9661, 2026-06-13) was silently auto-archived and never answered until he re-sent it 2026-06-17.

## Fix (FAIL-CLOSED) — 3 Changes

1. **All three `ollamaClassify` failure branches** now resolve `{category:'ACTION', priority:'medium', _classifyFailed:true}` with distinct reason (timeout/parse\_error/no\_json) — an unclassifiable email defaults to actionable, never FYI/archive.
2. **`matchStrategicPartner()` now runs independent of category** (guard `if (!ARGS.dryRun)`); on a partner match it elevates to ACTION via `emailInbox.updateClassification(...,'ACTION','high')`, sets partner\_tier, fires CEO push.
3. **Auto-archive is guarded** by `_skipArchiveDueToClassifyFail` and partner-elevated rows (cat patched to 'action') never reach the archive branch.

**New helper:** `updateClassification(id, classification, priority)` added + exported in `~/system/tools/email-inbox.js`.

## Verification

- `node --check` clean on both files
- Simulation harness `/tmp/evidence-103815/sim.test.js` = **39 PASS / 0 FAIL** incl. the exact Asmir/QODY regression
- Independent verification: **native verifier (7/7 atomic claims)** + **Proveo P2P PASS** (mesh-thr-95c7fb0b / mesh-msg-008f947c)

## Deployment

Daemon `com.john.email-agent` is `StartInterval` (spawns fresh node each cycle) → fix is **live on the next cycle**, no restart needed.

## Follow-on MC #103819 — RESOLVED (verified 2026-08-08)

The two heuristic INFO fallbacks **OUTSIDE `ollamaClassify`** now carry `_classifyFailed:true`:

- Circuit-breaker-open path, `daemons/email-agent.js:2526`: `{category:'INFO', priority:'low', summary:'Ollama circuit breaker open — fallback to INFO', _classifyFailed:true}`
- Promise-rejection catch path, `daemons/email-agent.js:2543`: `{category:'INFO', priority:'low', summary:'Classification failed — fallback to INFO', _classifyFailed:true}`

Both were actually implemented same-day as this page's parent fix, in commit `63142eb8cfa152436924f3a0a610e2c4095d0908` (2026-06-17 16:00 +0200, John AI) — the task's original line refs (~2161/~2177) were already stale by the time it was picked up again, since the file grew past 3000 lines in the interim. MC #103819 sat open only because the record was never closed with evidence; re-verified 2026-08-08 via `grep`, `node --check` (exit 0), and `git log -S` confirming no regression since. Evidence: `~/system/evidence/103819/verify-2026-08-08.md`.

## Lesson

**Email triage must FAIL-CLOSED** — an email the classifier could not process must never be silently archived; strategic-partner safety net must be category-independent.

---

*Evidence bundle:* `/tmp/evidence-103815/`  
*MC task:* #103815  
*Date:* 2026-06-17