All `.java` source files **must be UTF-8 without BOM** (no `EF BB BF` byte prefix).
- Maven's `javac` fails on BOM with `illegal character: '\uFEFF'`; IntelliJ IDEA tolerates BOM, so IDE-only compilation masks the problem until `mvn compile`.
- If `mvn compile` reports `illegal character: '\uFEFF'`, a tool has written the file with BOM. Strip it before retrying:
### Stream dropouts (`Error: Stream ended without finish_reason`)
The LLM's streamed (SSE) response was cut off before the terminal `finish_reason` event. It is **not** a bug in your code or in a tool — it is the upstream/relay dropping the connection mid-generation.
> **Sibling symptom — `Error: HTTP客户端等待超时` (HTTP client wait timeout).** Same root cause (relayed provider over `127.0.0.1`), different failure point: instead of the stream being *truncated mid-flight*, the client gives up *waiting for the response* (read timeout expires before first bytes / before completion). Leans toward **one over-heavy turn — upstream generates too slowly and the client read-timeout fires first**, or **the forwarder's idle timeout is shorter than generation time**. Same avoidance rules below; add: keep single turns small so first-byte latency stays under the client timeout, and prefer a retry.
> **Sibling symptom — `Error: DNS解析异常` (DNS resolution failure).** Same relay chain, but the *earliest* failure point: the request never even connected — the relay/upstream hostname could not be resolved (DNS query timed out / no answer / transient resolver hiccup). Unlike the two above this is **not correlated with turn size** — the request was never sent, so trimming the turn or `/compact` does **nothing**. Likely causes: local DNS resolver / hosts jitter, a VPN/proxy switch mid-session, the relay's own domain temporarily unresolvable upstream, or a brief network drop. **The only effective recovery is retry** (jitter self-heals); if it persists, check network / DNS / VPN — it is an environment/network fault, not something a repo change can fix.
This repo runs through a relayed provider (`PI_PROVIDER=new-provider`, `PI_MODEL=claude-opus-4-8`, forwarded via `127.0.0.1`), which makes dropouts more likely on large single turns.
**Most common triggers, worst first:**
- **Oversized single turn** — reading several long files at once (e.g. both `crm-lead/CONTEXT.md` + `crm-opportunity/CONTEXT.md`) then immediately doing a large write. The turn right after a big context dump drops most often.
- **One huge output** — emitting a large file (a full HTML report, hundreds of lines) in a single `write`.
- **Network / relay idle timeout** — the local forwarder or an nginx/VPN layer closing an idle SSE long-connection.
**How to avoid it:**
- Read large files with `offset`/`limit` in chunks; do not inhale whole long docs in one call.
- Write large files in small steps: `write` a skeleton, then append with successive `edit` calls, instead of one giant `write`.
-`/compact` or start a fresh session when the conversation history has grown large.
- Deterministic recovery is usually just **retry** — if the same action succeeds on a retry, it was relay jitter, not a real failure.