new file mode 100644
index 0000000..d6d6604
@@ -0,0 +1,134 @@
+---
+visibility: public
+---
+
+# PRD — picortex v1
+
+**Status:** Draft
+**Owner:** Jacob
+**Date:** 2026-04-23
+**Last reviewed:** 2026-04-23
+
+## 1. Problem
+
+Jacob wants to chat with Claude Code from his phone — via real iMessage, including group texts — the same way Cortex enables, but with a lighter-weight personal stack. The existing Cortex deployment is a shared business product carrying design constraints (Docker-container-per-workspace, session dashboard, multi-tenant billing) that aren't needed for a single user. Meanwhile, Cortex has just spent a month solving the actual hard problems (attention gating, per-chat isolation, sharing bridge, HMAC webhook ingestion, linq-sim harness) and all of those are directly reusable.
+
+There is no tool today that gives Jacob:
+1. A persistent Claude Code tmux session per iMessage conversation,
+2. Per-chat Unix-user isolation so group-chat compromise doesn't leak across,
+3. A mobile-first web UI to see the file tree and attach a live terminal mid-conversation,
+4. The lightest-weight spin-up possible so provisioning a new chat costs cents not dollars.
+
+## 2. Users & personas
+
+**v1 has one user: Jacob.** Everything else is future.
+
+Scenarios:
+- *Jacob in an Uber, wants to check whether the Cortex deploy succeeded.* Opens iMessage, texts the bot. Bot replies with status. No computer needed.
+- *Jacob in a group text with two collaborators, asks the bot to summarize the thread and file a ticket.* Bot responds inside the group thread (reply, not new message).
+- *Jacob at desk, wants to see what Claude Code is doing in his chat with "research-bot".* Opens web UI on laptop, clicks that chat, sees split view: messages on left, file tree + file viewer in middle, live terminal attached to the chat's tmux session on the right.
+- *Jacob wants to import a file from his personal chat into a group chat.* Group agent proposes the import; picortex DMs Jacob a challenge; Jacob replies "yes" from the DM; the file is copied with a `BridgeEvent` audit row.
+
+## 3. Goals
+
+**G1.** Text the bot from iMessage and get a useful Claude Code response within 10 seconds (warm chat) / 60 seconds (cold provision).
+**G2.** Every chat has its own Unix user + home dir; compromising a group chat's workspace cannot read Jacob's personal workspace.
+**G3.** Mobile Safari experience: a user on iPhone viewing any chat sees a usable, touch-first UI that does not require pinch-zoom.
+**G4.** Web terminal attaches to any chat's tmux session in < 2 s and displays the live Claude Code output.
+**G5.** linq-sim E2E suite covers at minimum: send/receive 1:1, send/receive group, reaction add/remove, reply-in-thread, attention-gating switch.
+**G6.** Operating cost of an idle chat is effectively zero (tmux session + a small home dir); an active chat's cost is dominated by the Anthropic API.
+
+## 4. Non-goals
+
+- **NG1.** No Docker / docker-compose / Kubernetes. [ADR-0002](../adrs/0002-linux-users-over-docker.md)
+- **NG2.** No Cortex-style session-management dashboard. A web terminal attach to the *current* chat is the entire session UI. [Constraint from user]
+- **NG3.** No billing, multi-tenant, or team accounts.
+- **NG4.** No native mobile apps. Mobile-first *web* UI only.
+- **NG5.** No group-chat-specific mobile UI in v1 — 1:1 threading works; groups render on desktop first.
+- **NG6.** No MCP tools for cross-chat queries in v1 ([Cortex R6](../wiki/cortex-inheritance.md#r6-mcp-cross-chat) deferred; sharing bridge R7 is enough).
+- **NG7.** No OpenClaw. No OpenClaw. No OpenClaw. ([explicit user directive])
+- **NG8.** No shared knowledge graph backend yet. Canonical log is SQLite on the picortex server. Integration with noos deferred.
+
+## 5. User stories
+
+- **US-1.** As Jacob, I text my iMessage-only bot "What's the status of the ListHub deploy?" and get a coherent answer that used `run_command` on the server.
+- **US-2.** As Jacob in a group text with two friends, I @mention the bot ("@picortex summarize this"). The bot replies *in the thread* (not a new message) with a 3-bullet summary.
+- **US-3.** As Jacob, I change a group's attention mode to "discriminate" and the bot stops responding to off-topic messages but still speaks up when the conversation is about something it can help with.
+- **US-4.** As Jacob on mobile Safari, I tap a chat and see messages, swipe-right gets file browser, swipe-further-right gets live terminal.
+- **US-5.** As Jacob, I ask in a group chat "bring in the `refs/2026-plan.md` from my DM workspace." The bot DMs me a challenge; I reply "yes"; the file appears in the group chat's workspace and a `BridgeEvent` is logged.
+- **US-6.** As Jacob deploying picortex, I run `./deploy.sh` and the latest version appears in the UI's footer within seconds, with an update-available badge cleared.
+
+## 6. Functional requirements
+
+Numbered for citation. Every one is pass/fail testable.
+
+### Linq integration
+- **FR-1.** Bot receives inbound iMessage via `POST /api/linq/inbound`, signature verified with `HMAC-SHA256("{timestamp}.{raw_body}", LINQ_WEBHOOK_SECRET)`. Reject unsigned / skew > 5 min / replay.
+- **FR-2.** Bot sends replies via `POST $LINQ_BASE_URL/api/partner/v3/sendMessage`.
+- **FR-3.** Bot supports all event types the linq-sim publishes: `message.{received, delivered, read, edited, failed}`, `reaction.{added, removed}`, `chat.typing_indicator.{started, stopped}`, `chat.{created, updated, group_name_updated}`, `participant.{added, removed}`.
+- **FR-4.** Bot supports in-thread replies (`data.reply_to_message_id`). *Requires linq-sim to be upgraded — see [Stage S2](../plans/2026-04-23-initial-roadmap.md#s2-linq-sim-thread-support).*
+
+### Workspace isolation
+- **FR-5.** Every durable chat ID maps to exactly one Unix user (`chat-<8-hex-of-sha256>`) and one home dir (`$CHAT_WORKSPACE_ROOT/<chat_id>`).
+- **FR-6.** Home dirs are `chmod 0700`, owned by the chat user. No other chat user can read them.
+- **FR-7.** The picortex backend runs as a privileged service user; shells into each chat's user via `sudo -u chat-<hex> -H …` (no shared filesystem write path).
+- **FR-8.** Backend SQLite is the **canonical** message log. The workspace FS is a **cache only** and is never used for authorization.
+
+### Sessions
+- **FR-9.** Each chat has a tmux session named `picortex:<chat_id>`. If missing, backend creates it on first inbound message.
+- **FR-10.** The tmux session runs `claude --dangerously-skip-permissions` (or equivalent) with cwd = chat home dir. *Flag choice TBD in S3.*
+- **FR-11.** Idle tmux sessions are killed after 7 days of zero activity; home dir preserved for 30 days, then archived.
+- **FR-12.** Web terminal (xterm.js) attaches to a chat's tmux session via `tmux attach -t picortex:<chat_id>` through a backend WebSocket PTY bridge.
+
+### Attention gating
+- **FR-13.** Each chat has an attention mode: `always | mentions-only | discriminate | discriminate-quiet | silent`. Default for 1:1: `always`; default for groups: `mentions-only`.
+- **FR-14.** Mode `discriminate` runs an LLM over the message with a git-versioned prompt at `$CHAT_HOME/.picortex/prompts/discriminator.md` and only proceeds if the model scores ≥ threshold.
+- **FR-15.** Mode changes are visible in the mobile UI's chat-settings panel.
+
+### Sharing bridge
+- **FR-16.** When chat A asks to import a file from chat B, backend DMs the user-intersection (same Jacob here) a challenge; only a reply from that DM approves the import.
+- **FR-17.** Every bridge op logs a `BridgeEvent` row (source chat, dest chat, file path, sha256, approver user, timestamp).
+
+### Mobile-first web UI
+- **FR-18.** Mobile Safari: messages pane is usable without pinch-zoom; tap-targets ≥ 44pt; `<meta viewport>` correct.
+- **FR-19.** Tabbed / swipe-panel layout on mobile: `[Messages | Files | Terminal]`.
+- **FR-20.** In-thread replies render as a "replying to …" pill above the message.
+- **FR-21.** Footer shows `picortex vX.Y.Z` from `package.json`. When `HEAD` on `main` is newer than running commit, a dot badge appears next to the version.
+
+### Observability
+- **FR-22.** All server logs are structured JSON via `pino`.
+- **FR-23.** Every HTTP request has an `X-Request-ID` header, echoed to the response.
+- **FR-24.** `POST /api/frontend-log` accepts browser error reports and writes them to server logs with the originating `X-Request-ID`.
+- **FR-25.** `/health` returns 200 + `{version, uptime, db_ok}`.
+
+## 7. Non-functional requirements
+
+- **NFR-1.** Cold-start new chat: provision Unix user, home dir, tmux session, first Claude turn — **< 60 s**.
+- **NFR-2.** Warm response (existing tmux, cached Anthropic session): **< 10 s** first token.
+- **NFR-3.** Idle cost: **< 50 MB RAM per paused chat** (tmux + bash, no Claude process).
+- **NFR-4.** Server handles **≥ 50 simultaneous chats** on a 4 GB VPS.
+- **NFR-5.** Accessibility: WCAG AA contrast, keyboard nav for desktop web UI.
+- **NFR-6.** Secrets never written to disk in plaintext outside `.env`.
+
+## 8. Open questions
+
+| # | Question | Owner | Status |
+|---|---|---|---|
+| Q1 | What Claude Code command-line flag set do we run tmux sessions with? `--dangerously-skip-permissions` inside the sandboxed Unix user? | Jacob | Open — revisit in S3 |
+| Q2 | Do we need a backend read model (search) for message history or is grep-over-SQLite enough? | Jacob | Open — revisit post-S5 |
+| Q3 | What's the deployment target: Hetzner (jcortex sibling), Fly.io, or Mac Mini (HMA)? | Jacob | Open — revisit in S7 |
+| Q4 | OpenChat: upgrade to linq-adapter as a **separate** project (OpenChat-yg8 continues), or roll into picortex repo? | Jacob | Open — see [wiki/openchat-adapter.md](../wiki/openchat-adapter.md) |
+| Q5 | Does picortex eventually federate with noos (knowledge graph backend)? | Jacob | Open — see [wiki/relationship-to-noos.md](../wiki/relationship-to-noos.md) |
+| Q6 | What's the real project name? `picortex` is provisional. | Jacob | Open — rename before v0.1.0 |
+
+## 9. Success metrics
+
+- **M1.** Jacob uses picortex for ≥ 1 productive conversation per day for 2 consecutive weeks.
+- **M2.** Zero isolation breaches over the first 3 months (measured by: no chat user accessing another chat's home dir).
+- **M3.** Median cold-start time < 60 s (p95 < 120 s).
+- **M4.** Median warm-reply time < 10 s (p95 < 30 s).
+- **M5.** Uptime ≥ 99 % over a 30-day window.
+
+## 10. Out of scope (reiterated)
+
+Docker, Kubernetes, dashboard, billing, multi-tenant, native apps, group-chat mobile UI, cross-chat MCP, knowledge-graph integration, native iOS push.
\ No newline at end of file