AGENTS.md — picortex
Operational instructions for coding agents (Codex CLI, Claude Code, Cursor, Amp, etc.). Prepended to every conversation — keep this file lean. For any detail beyond the essentials, link out and let the agent fetch on demand.
Project overview
picortex is a personal variant of Cortex for iMessage + group texting through the Linq partner API. It spawns Claude Code sessions per chat, isolated by Linux user + filesystem permissions (not Docker), with a mobile-first web UI for monitoring, file browsing, and live terminal attach.
Name is provisional.
Status
Planning phase (2026-04-23, post-pivot). The project has PRD, plan, ADRs, specs, and an LLM wiki — but no code yet.
⚠️ Don't start implementation at S1. The initial roadmap (S1-S9) was built around Option 1 (tmux-centered single-host). Late in the planning session, Jacob reframed the product as "awesome texting experience" and reopened the architecture choice — see docs/plans/2026-04-23-prototype-options.md. The actual next action is Q0 (picortex-adb) — define the success criteria that constrain the architecture choice. Then Q1-Q4 choose the option bundle, then picortex-357 reconciles the docs with the chosen option, then a revised roadmap replaces S1-S9.
Primary doc pointers (progressive disclosure)
Don't inline content from these — link and let the reader fetch:
- PRD: docs/prd/001-picortex-v1.md
- Roadmap: docs/plans/2026-04-23-initial-roadmap.md
- Architecture: docs/wiki/architecture.md
- Cortex inheritance map: docs/wiki/cortex-inheritance.md
- ADRs: docs/adrs/
- Specs: docs/specs/
- LLM wiki (Karpathy-style): docs/wiki/index.md
Dev environment
- Node 20+, tmux 3.3+, ripgrep, sudo, SQLite 3.40+
npm installcp .env.example .envthen fill inLINQ_API_KEY,LINQ_WEBHOOK_SECRET,ANTHROPIC_API_KEY(or rely onclaudeCLI auth)npm run dev— backend (7823) + frontend (7824) + linq-sim orchestrator
Stack
- Backend: Node.js + Fastify (TypeScript, strict mode)
- Frontend: Vite + React + Tailwind (mobile-first)
- Terminal: xterm.js (client),
node-pty+tmux(server) - Storage: SQLite (canonical message log, per-chat config); per-chat filesystem workspaces at
/srv/picortex/chats/<chat_id>/ - Auth (web UI): Noos OAuth SSO; the iMessage/Linq path does not require UI auth
- Observability:
pinoJSON logs,X-Request-IDmiddleware,/api/frontend-logbrowser error forwarder
Ports
- Dev backend: 7823
- Dev frontend: 7824
- linq-sim (from Cortex): 8447
- Never use 3000/5000/8080/8000. See Jacob's
~/.claude/rules/dev-patterns.md.
Code style
- TypeScript strict. No
anywithout a comment justifying it. - Conventional commits (
feat:,fix:,docs:,chore:,refactor:,test:). - One concern per PR. Reference beads ticket:
[picortex-xxx]. - Line length: soft 100, hard 120.
Testing
- vitest for unit + integration
- E2E runs against linq-sim, not real Linq
npm test— unitnpm run test:e2e— E2E (starts a linq-sim instance)- A feature isn't done until: (a) tests pass, (b) a linq-sim scene demo exists, (c) beads ticket closed.
Security invariants
- Canonical message log lives on backend (SQLite). Per-chat workspace filesystem is a cache, never authoritative. (Cortex R5.1)
- Every cross-chat op (e.g. "import X from my personal chat into this group") requires an out-of-band challenge/response: the group agent DMs the user, the user's reply is the approval. (Cortex R5.4-5.5)
- Linq inbound webhooks verified with
HMAC-SHA256("{timestamp}.{raw_body}", LINQ_WEBHOOK_SECRET). Reject on bad signature, skew > 5 min, or replay. - No secrets in workspace FS. Env vars stay in the backend process. Per-chat Unix users have no ambient credentials.
- Per-chat Unix user can't see other chats' files — enforced by POSIX permissions (owner-only home dirs, 0700). See ADR-0002.
- Never disable isolation for convenience. If provisioning breaks, stop and ask; do not fall through to "run as shared user."
Commit / PR conventions
- Branch:
<type>/<short-desc>e.g.feat/attention-discriminator - PR body: Summary, Test plan, beads link
- Squash merge preferred
Deployment
TBD — candidates: Hetzner VPS, Fly.io, HMA (Mac Mini). See docs/runbooks/deploy.md once chosen.
Dev patterns (per Jacob's global rules)
- Version on screen: display app version in user-menu footer (from
package.json) - Update-available indicator: non-intrusive badge when a newer commit is on
main - Frontend error logging:
POST /api/frontend-logendpoint that forwards browser errors to the server's structured log - Port conflict handling: auto-increment if 7823/7824 are taken, display actual port
- System deps: proactively detect
tmux,rg,sudo,sqlite3; printbrew install …hint if missing
Beads
- Prefix:
picortex- - Init:
bd init picortex(done at project setup) - List open:
bd list --status=open - Create:
bd create "Title" --type task|feature|bug|epic --priority 0-4
Cortex inheritance rule
Where Cortex already solved it, inherit, don't re-derive. See docs/wiki/cortex-inheritance.md for the requirement-by-requirement map.
Cortex research cutoff (revised 2026-04-23): Don't inherit patterns from pre-af3a76f5 Cortex (the Piyush-era EC2/SSH/Vercel design, 2026-01-20 → 2026-01-23), but do study it deliberately — several of its patterns (bot/workspace physical split, claude -c -p per turn) are exactly what the texting-first picortex wants. See docs/wiki/piyush-era-design.md and docs/plans/2026-04-23-prototype-options.md (Option 2).
Landing the Plane (Session Completion)
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds