Create docs/specs/005-attention-gating.md
73a3382ed4b7 jacobcole 2026-04-23 1 file
new file mode 100644
index 0000000..794bd85
@@ -0,0 +1,85 @@
+---
+visibility: public
+---
+
+# Spec 005 — Attention gating
+
+**Status:** Draft
+**Related:** [PRD FR-13..FR-15](../prd/001-picortex-v1.md#attention-gating), [Wiki: attention-gating](../wiki/attention-gating.md)
+
+## Goal
+
+In group chats especially, the bot should speak only when wanted. Adopt Cortex R4's five-level ladder.
+
+## Modes
+
+| Mode | Behavior |
+|---|---|
+| `always` | Respond to every message. Default for 1:1 DMs. |
+| `mentions-only` | Respond only if `@picortex` appears or message is a direct reply to a bot message. Default for groups. |
+| `discriminate` | Run LLM classifier on message; respond iff `should_respond == true`. |
+| `discriminate-quiet` | Same as `discriminate` but if `should_respond == false`, also skip reactions/typing indicators. |
+| `silent` | Record only; never respond. Useful for eavesdropping / training data. |
+
+## Rule-first pipeline
+
+Before the LLM discriminator runs, hard rules evaluate in order:
+
+1. **Slash command** (`/picortex <x>`) → always respond (mode-independent).
+2. **Direct reply to a bot message** → always respond.
+3. **`@picortex` mention** → respond.
+4. **Admin override message from Jacob's DM** → respond (even in `silent` mode).
+5. **Non-text payload** (image, voice memo) → depends on mode; `always` responds, others skip.
+
+If no rule fires and mode is `discriminate`, run the LLM classifier. If mode is `mentions-only` and no rule fired, skip.
+
+## LLM discriminator
+
+- Prompt lives at `$CHAT_HOME/.picortex/prompts/discriminator.md`, git-tracked inside the chat's home.
+- Default template seeded on provisioning (from `/usr/local/share/picortex/discriminator.default.md`).
+- Prompt receives: last N=6 messages (user+bot), the new message, chat metadata.
+- Response must be JSON: `{"should_respond": bool, "reason": "..."}`.
+- Model: default `claude-3-5-haiku` via Anthropic API (cheap + fast).
+- Failure: if the model returns invalid JSON or times out (> 5 s), fail-open (respond) for 1:1, fail-closed (skip) for groups.
+
+Cost budget: < $0.001 per classification.
+
+## Configuration UI
+
+In chat settings panel (Spec 007):
+
+- Current mode (radio)
+- "Edit discriminator prompt" → opens file-browser on `.picortex/prompts/discriminator.md`
+- "Test discriminator" → run on recent N messages and show decisions
+- "Version history" → `git log` on the chat's home repo
+
+## Admin commands
+
+Parsed from message body:
+
+- `/picortex attention always|mentions-only|discriminate|discriminate-quiet|silent`
+- `/picortex attention show` — print current mode + prompt path
+- Only accepted from the chat owner (Jacob's phone number).
+
+## Schema
+
+```sql
+CREATE TABLE chat_config (
+ chat_id TEXT PRIMARY KEY,
+ attention_mode TEXT NOT NULL DEFAULT 'mentions-only',
+ discriminator_model TEXT DEFAULT 'claude-3-5-haiku',
+ discriminator_threshold REAL DEFAULT 0.5,
+ updated_at INTEGER NOT NULL
+);
+```
+
+## Testing
+
+- **Unit:** rule pipeline (mention detection, slash command parsing).
+- **Integration:** discriminator prompt harness with golden tests.
+- **E2E:** linq-sim group; toggle modes; assert responses vs non-responses.
+
+## Open questions
+
+- OQ1: How to expose "why did you respond / not respond" to Jacob? (Debug panel showing discriminator reason.)
+- OQ2: Per-user attention overrides inside a group? (v0.2.)
\ No newline at end of file