# TODO
Owner-visible future work for Personal Context Protocol. Keep this list focused on versioned product work, not per-round scratch notes.
## v0.1 Completed
- [x] **Core schema and API**
- [x] App instance and UI auth tables
- [x] Topic, session, session token, message, event, and migration tables
- [x] Setup, auth check, topic, session, token, message, event, and export routes
- [x] API route handlers served from `src/app/api/v1`
- [x] API routes marked dynamic so builds do not execute runtime database handlers
- [x] **Setup and authentication**
- [x] Deploy initialization creates schema, app instance, and the first admin credential
- [x] One-time fallback setup token returned in the browser setup response
- [x] Admin login page prompts for the UI token before dashboard access
- [x] Admin login page prompts for fallback initialization when the database is not initialized
- [x] `/api/v1/auth/check` validates the UI token before saving it in browser storage
- [x] Direct `/dashboard` access without `ui_token` redirects to `/login?next=/dashboard`
- [x] Automatic Postgres schema bootstrap when setup status/init runs with `DATABASE_URL`
- [x] **Session workspace UI**
- [x] Topic creation and selection
- [x] Session creation, editing, moving, archiving, and restoring
- [x] Session token generation with copy-to-clipboard
- [x] Message and event review views
- [x] Dashboard split into focused topic, session, and workspace components
- [x] **Frontend shell**
- [x] Public introduction page
- [x] Light, dark, and system theme toggle
- [x] Published documentation links use GitHub Pages
- [x] Footer links for documentation, API reference, deployment, and future work
- [x] **Diagnostics and docs**
- [x] Error messages include consequence, module/process, and cause
- [x] Build/runtime diagnostics identify missing required env vars without printing secrets
- [x] Build/start deploy initialization prints PCP-generated first-login admin tokens when `PCP_ADMIN_TOKEN` is absent
- [x] Sphinx documentation build root fixed
- [x] README and protocol docs updated for implemented routes
## v0.1.3 Completed — Agent recording URL
Shipped within the v0.1 line. The agent recording-URL surface and token
expiration land here as part of v0.1; the move to **v0.2.0 is gated on
completing v0.1 Remaining Hardening below**, not on shipping this feature.
- [x] **Recording URL + token model**
- [x] `GET /r/:sessionId` public recording-URL entry point
- [x] `GET /api/v1/agent/resolve?url=` and `GET /api/v1/agent/sessions/:id/protocol`
- [x] Self-describing protocol (auth, routes, allowed_actions, limits)
- [x] Copyable Recording URL + Access Token pair in the session UI
- [x] **Agent ingestion**
- [x] `POST /api/v1/agent/sessions/:id/messages` append (append-only)
- [x] `POST /api/v1/agent/sessions/:id/compact` durable summaries (additional records)
- [x] `POST /api/v1/agent/sessions/:id/ingest` forgiving parser (JSON, PCP tags, ChatML, transcript)
- [x] Structured `{ ok, code, retryable, message, next_steps }` agent errors
- [x] **Token expiration**
- [x] `expires_in` choices (1h/24h/7d/30d/never), default 7d, NULL = never expire
- [x] Reject expired tokens; show active/expired/revoked + last used in UI
- [x] **Naming**
- [x] One-click topic/session creation with generated default names
- [x] Duplicate title auto-suffixing; AI-suggested title normalization
## v0.1.4–v0.1.10 Completed — Operability & correction
Shipped within the v0.1 line (still gated on v0.1 Remaining Hardening before v0.2).
- [x] **Admin token rotation** (v0.1.4–v0.1.6): per-deploy rotation unless set in
Settings (`user`) or `PCP_ADMIN_TOKEN` (`env`); recovery guidance + dashboard warning
- [x] **Public sessions** (v0.1.7): `sessions.public`, read-only `/s/:id` + public API; 404 for private
- [x] **Token management** (v0.1.7, v0.1.9): revoke + rename via PATCH; auto-distinct token names; dashboard modal
- [x] **Recording URL origin fix** (v0.1.7): UI builds from `window.location.origin`; server ignores non-absolute `PCP_APP_URL`
- [x] **Front-page polish** (v0.1.7): load animations; footer shows deployed version
- [x] **Uncategorized sessions + ChatGPT-style nav** (v0.1.5): nullable `topic_id`; collapsible groups; right-click/long-press menu
- [x] **Human fallback import** (v0.1.8): paste agent fallback block; dedup-merge already-recorded messages
- [x] **Recording modes** (v0.1.9): `sessions.mode` wild/exact surfaced to the agent
- [x] **Message correction** (v0.1.10): human admin edit/delete (append-only still enforced for AI);
multi-select with select-all + shift-range; long messages fold to 5 lines in select mode
## v0.1 Remaining Hardening
- [x] **Startup validation**
- [x] Add shared configuration validator for `DATABASE_URL`, `PCP_INSTANCE_SECRET`, and `PCP_APP_URL`
- [x] Validate malformed values, not only missing values
- [x] Surface deployment configuration errors in the setup UI without exposing secrets
- [ ] **Setup integration tests**
- [ ] Missing `DATABASE_URL`
- [ ] Unreachable Neon database
- [ ] Empty database schema bootstrap
- [ ] Duplicate initialization
- [ ] Invalid admin token login attempt
- [ ] **Security audit**
- [ ] Resolve current `npm audit` findings, including the Next.js 14.2.5 warning
- [ ] Add CI security scanning
- [ ] Add automated checks that logs never include plaintext admin or session tokens
- [ ] **API docs sync**
- [x] Generate or validate docs against `src/app/api/v1` (drift test)
- [ ] Add CI route/doc drift check
- [ ] Keep README endpoint list in sync with protocol docs
- [x] **Agent import visibility and compaction rendering**
* Context: A user pasted a valid `PCP_COMPACT` block into the import flow. The UI reported `Imported a compaction block`, but the session message view showed no readable conversation content.
* Trigger: Import a `PCP_COMPACT` payload that contains `summary`, `timeline`, `decisions`, or `requirements`, but no `messages` array.
* Missing behavior: The app stores the compaction but does not make it visible in the session workspace as first-class readable content.
* Expected UI output:
* The session workspace has a `Compactions` tab or section next to `Messages` and `Events`.
* If the session has compactions but no messages, the Messages pane shows: `No raw messages recorded. This session has compact summaries.`
* The Compactions pane shows cards with `summary`, `timeline`, `decisions`, `requirements`, `open_questions`, `artifacts`, and `warnings` when present.
* Expected API/import response:
```json
{
"ok": true,
"imported": {
"messages": 0,
"compactions": 1,
"events": 1
},
"session_id": "ses_...",
"notice": "Imported a compact summary. No raw messages were included."
}
```
* Implementation tasks:
* [x] Add or verify a `compactions` table/model.
* [x] Render compactions in the session workspace.
* [x] Make `PCP_COMPACT` with no messages store a compaction, not fake message rows.
* [x] Make `PCP_COMPACT` (and `PCP_INGEST`) with a `messages` array import both messages and the compaction.
* [x] Add an audit event for compact import (compaction.recorded).
* [x] Add tests for compact-only import and compact-with-messages import.
* [ ] **Canonical agent fallback format**
* Context: Different agents are returning incompatible fallback formats. Some return generic JSON, some return `PCP_COMPACT`, and some omit fields required for message recording.
* Trigger: Give an agent a Recording URL and Access Token, then ask it to record a session when direct upload is unavailable.
* Missing behavior: The generated prompt does not force a single canonical fallback shape for message-level recording.
* Expected output from agents when API upload fails but messages are reconstructable:
```text
{
"schema_version": 1,
"session_id": "ses_...",
"session_title": "optional suggested title or null",
"messages": [
{
"role": "user | assistant | system | tool | unknown",
"content": "",
"content_type": "text/markdown",
"created_at": null,
"observed_at": "ISO timestamp",
"provider": "chatgpt | claude | gemini | cursor | codex | unknown",
"agent_name": "unknown",
"base_model": "unknown",
"attachments": [
{
"title": "",
"media_type": "",
"download_url": "",
"description": ""
}
],
"metadata": {}
}
],
"compaction": null
}
```
* Expected output from agents only when message-level reconstruction is impossible:
```text
{
"schema_version": 1,
"session_id": "ses_...",
"session_title": "optional suggested title or null",
"summary": "",
"timeline": [],
"decisions": [],
"requirements": [],
"open_questions": [],
"artifacts": [
{
"title": "",
"media_type": "",
"download_url": "",
"description": ""
}
],
"warnings": [],
"provider": "unknown",
"agent_name": "unknown",
"base_model": "unknown",
"created_at": "ISO timestamp"
}
```
* Implementation tasks:
* [ ] Update the generated agent instruction to prefer direct upload first, `PCP_INGEST` second, and `PCP_COMPACT` only as last resort.
* [ ] Update `/api/v1/agent/sessions/:id/ingest` to prefer `PCP_INGEST`.
* [ ] Keep backward compatibility with `PCP_APPEND`.
* [ ] Return structured errors when payloads omit required message fields.
* [ ] Add parser tests for `PCP_INGEST`, legacy `PCP_APPEND`, and `PCP_COMPACT`.
* [ ] **Agent upload diagnostics and MCP connection guidance**
* Context: Some agents can discover `record_messages`, `record_compact`, and `ingest_any`, but cannot complete upload because their execution environment cannot resolve `pcp.trance-0.com` or cannot send `Authorization: Bearer ...` POST requests.
* Trigger: An agent replies that it discovered the protocol but cannot claim recording success because upload failed.
* Missing behavior: The app does not give the user a precise diagnosis or a next operational path.
* Expected UI output:
* In the Token / Agent Instruction panel, show a `Connectivity test` button.
* The test checks:
* recording URL resolves
* protocol endpoint responds
* authenticated `review` endpoint works
* authenticated dry-run ingest works if implemented
* On failure, show one of:
* `DNS resolution failed`
* `TLS connection failed`
* `Protocol endpoint unreachable`
* `Token rejected`
* `Upload route rejected`
* `Payload schema rejected`
* Expected API response for diagnostics:
```json
{
"ok": false,
"stage": "dns | tls | protocol | auth | upload | schema",
"code": "DNS_RESOLUTION_FAILED",
"retryable": false,
"message": "The recording host could not be resolved from this environment.",
"next_steps": [
"Verify public DNS for pcp.trance-0.com.",
"Try upload from a local relay or MCP-enabled agent."
]
}
```
* Implementation tasks:
* [ ] Add documentation for connecting `https://pcp.trance-0.com/mcp` as an MCP server where supported.
* [ ] Add sample Codex MCP config using `bearer_token_env_var`.
* [ ] Add sample local relay script that posts `PCP_INGEST` from a file.
* [ ] Document that prompt-only ChatGPT cannot be forced to send custom authenticated POST requests unless connected through MCP/tools/actions.
* [ ] Do not describe CORS or trusted origins as a solution for model-side POST capability.
* [ ] **Message viewport containment**
* Context: Current message panes can overflow the browser window and push other widgets out of view.
* Trigger: Open a session with long messages, long code blocks, or many events.
* Missing behavior: The app shell does not constrain message/event/session panels to the viewport.
* Expected UI output:
* Header, sidebar, session workspace, message list, event list, and token widgets remain inside the browser viewport.
* Message list scrolls internally with `overflow-y: auto`.
* Event list scrolls internally with `overflow-y: auto`.
* Session/sidebar list scrolls internally with `overflow-y: auto`.
* Long code blocks scroll horizontally inside the message bubble instead of widening the page.
* Implementation tasks:
* [ ] Convert the dashboard/workspace shell to a viewport-height layout.
* [ ] Add internal scroll containers for message, event, and session lists.
* [ ] Prevent page-level overflow except on narrow screens where intentional.
* [ ] Add regression test or visual check using a long message and a long code block.
* [ ] **ChatGPT-like message alignment**
* Context: Message alignment is currently wrong. System, user, and assistant messages are not visually differentiated in a readable chat flow.
* Trigger: Open any recorded session with user and assistant messages.
* Missing behavior: Message role is not mapped to consistent layout.
* Expected UI output:
* `system`: full-width horizontal card.
* `user`: right-aligned bubble.
* `assistant` or `agent`: left-aligned bubble.
* `tool` or `unknown`: neutral full-width or left-aligned card with visible role label.
* All bubbles wrap long text.
* Code blocks scroll horizontally inside the bubble.
* Implementation tasks:
* [ ] Add role-to-layout mapping.
* [ ] Add role labels for non-user/non-assistant messages.
* [ ] Add CSS tests/snapshots or component tests for role alignment classes.
* [ ] Verify markdown and code rendering do not break layout.
* [ ] **Session movement between topics**
* Context: Users need to reorganize sessions after creation. Topic management is human-only, but the UI should allow a session to be reassigned to another topic/folder.
* Trigger: User drags a session from one topic group to another in the sidebar.
* Missing behavior: Sidebar does not support drag-to-reassign or does not persist the session��s new topic.
* Expected UI output:
* Dragging a session over a topic highlights the target topic.
* Dropping the session moves it under the target topic.
* A toast appears: `Moved session to .`
* The session ID remains unchanged.
* The message list remains unchanged.
* Expected database/API behavior:
* Update only `sessions.topic_id`.
* Append an event with kind `session_moved_topic`.
* Reject this operation for AI session tokens.
* Implementation tasks:
* [ ] Add admin-only API route or server action for moving a session to another topic.
* [ ] Add drag-and-drop in the sidebar.
* [ ] Add audit event for the move.
* [ ] Add tests that UI/admin auth can move sessions.
* [ ] Add tests that AI tokens cannot move sessions.
* [ ] **Sidebar scaling and lazy session rendering**
* Context: Topics with many sessions make the sidebar hard to scan and may render too many items at once.
* Trigger: Create or import many sessions under one topic.
* Missing behavior: The sidebar renders too many sessions and lacks progressive disclosure.
* Expected UI output:
* Each topic initially shows the 5 most recently updated sessions.
* A `Show more` button appears when more sessions exist.
* Clicking `Show more` reveals the next batch.
* Sessions are sorted by `last_message_at` first, then `updated_at`, descending.
* Sidebar scrolls internally and does not push the page height.
* Implementation tasks:
* [ ] Limit initial session render to 5 per topic.
* [ ] Add per-topic `Show more`.
* [ ] Use server-side limit/pagination where practical.
* [ ] Add tests for initial 5-session render and expansion.
* [ ] **Precise topic and session default naming**
* Context: Topic/session creation can produce blank, duplicate, or confusing names. User wants one-click creation without being forced to name the object first.
* Trigger: User clicks `New Topic` or `New Session` repeatedly.
* Missing behavior: Name generation is not deterministic and duplicate-safe enough.
* Expected UI output:
* Topic defaults:
* `New Topic`
* `New Topic 2`
* `New Topic 3`
* Session defaults within a topic:
* `New Session`
* `New Session 2`
* `New Session 3`
* If a human enters duplicate topic title `Research`, create or suggest `Research 2` instead of blocking.
* If an AI suggests duplicate session title `Route B Build Prompt`, normalize to `Route B Build Prompt 2`.
* Implementation tasks:
* [ ] Implement `normalizeTitle(input)`.
* [ ] Implement `dedupeTitle(baseTitle, existingTitles)`.
* [ ] Implement `generateUniqueTopicTitle(baseTitle)`.
* [ ] Implement `generateUniqueTopicSlug(baseTitle)`.
* [ ] Implement `generateUniqueSessionTitle(topicId, baseTitle)`.
* [ ] Add tests for blank titles, repeated default titles, and duplicate AI-suggested titles.
* [ ] **Session content search**
* Context: Current search is insufficient if it only searches session title. Users need to find sessions by message content and compact summaries.
* Trigger: Search for a keyword that appears inside a recorded message or compaction but not in the session title.
* Missing behavior: Search does not inspect message content or compaction content.
* Expected UI output:
* Add independent `Search Sessions` modal or page.
* Results include session title, topic title, match type, and snippet.
* Snippet includes roughly 20 words before and after the match.
* Matched terms are highlighted.
* Clicking a result opens the session and scrolls/highlights the target message or compaction when possible.
* Expected API:
```http
GET /api/v1/search/sessions?q=&limit=20&offset=0
```
```json
{
"results": [
{
"session_id": "ses_...",
"session_title": "...",
"topic_id": "topic_...",
"topic_title": "...",
"match_type": "message | compaction | title",
"message_id": null,
"compaction_id": null,
"snippet": "...",
"highlight_ranges": [
{ "start": 10, "end": 18 }
],
"updated_at": "ISO timestamp"
}
],
"next_offset": null
}
```
* Authorization:
* UI/admin token can search all sessions.
* AI session token cannot search all sessions.
* Implementation tasks:
* [ ] Search session titles.
* [ ] Search message content.
* [ ] Search compaction summary, timeline, decisions, requirements, and open questions.
* [ ] Add keyword highlighting.
* [ ] Add result navigation to target session/message/compaction.
* [ ] Use Postgres full-text search if low-risk; otherwise use safe `ILIKE` for v0.1 and keep the API upgradeable.
* [ ] Add tests for message-content search, compaction search, snippets, highlights, and authorization.
* [ ] **Structured agent error contract**
* Context: Agents recover poorly from vague server errors and may reply with inconsistent fallback blocks.
* Trigger: Send malformed `PCP_INGEST`, expired token, forbidden topic mutation, or oversized payload.
* Missing behavior: Errors are not consistently actionable for an AI agent.
* Expected API error shape:
```json
{
"ok": false,
"code": "INVALID_INGEST_SCHEMA",
"retryable": true,
"message": "The payload did not match PCP_INGEST or PCP_COMPACT.",
"next_steps": [
"Retry with PCP_INGEST if you can reconstruct messages.",
"Retry with PCP_COMPACT if only a summary is available."
]
}
```
* Required error cases:
* `TOKEN_EXPIRED`
* `TOKEN_REVOKED`
* `TOKEN_SCOPE_FORBIDS_ACTION`
* `TOPIC_MANAGEMENT_FORBIDDEN`
* `INVALID_INGEST_SCHEMA`
* `PAYLOAD_TOO_LARGE`
* `MESSAGE_TOO_LARGE`
* `UNSUPPORTED_CONTENT_TYPE`
* Implementation tasks:
* [ ] Normalize all agent-facing route errors to this shape.
* [ ] Ensure errors never echo the access token.
* [ ] Add tests for each required error case.
* [ ] **Acceptance checks for this hardening batch**
* [ ] `npm run typecheck` passes.
* [ ] `npm run test` passes.
* [ ] `npm run build` passes.
* [ ] Importing compact-only content makes a visible compaction card.
* [ ] Importing message-shaped fallback content creates visible chat messages.
* [ ] Long messages and events scroll inside the viewport.
* [ ] User, assistant, system, tool, and unknown roles render with the expected alignment.
* [ ] Recording URL + Access Token flow still works.
* [ ] AI tokens cannot manage topics, move sessions, or search all sessions.
* [ ] User can drag sessions between topics.
* [ ] Sidebar only shows the first 5 recent sessions per topic until `Show more`.
* [ ] Search finds keywords inside message content and compactions.
* [ ] Structured errors are returned for malformed agent uploads.
## v0.2 Protocol Features
- [ ] **Token management**
- [ ] Token revocation UI
- [ ] Token revocation API flow
- [ ] Audit events for token revocation
- [x] Manual custom admin token from the settings page
- [x] Recovery path for lost UI token through `PCP_ADMIN_TOKEN` without printing tokens to logs
- [x] Missing admin credential repair during setup/init
- [ ] Full UI token rotation flow with audit event history
- [ ] **Admin features**
- [ ] Replace browser-local token storage if stronger admin unlock semantics are needed
- [ ] Direct topic detail endpoint
- [ ] Direct message read endpoint
- [ ] Export download controls in the admin UI
- [ ] **Import/export**
- [ ] Import endpoint for PCP JSON
- [ ] Import endpoint for PCP JSONL
- [ ] Import endpoint for generic transcripts
- [ ] Richer transcript export formats
- [ ] **Query improvements**
- [ ] Pagination for topics
- [ ] Pagination for sessions
- [ ] Pagination for messages
- [ ] Pagination for event logs
- [ ] Cross-session search
- [ ] **Advanced context features**
- [ ] Bulk operations
- [ ] Attachment support
- [ ] Semantic search after the core append-only workflow is stable
- [ ] Summaries and context compression
- [ ] Auto-tagging
## v0.3 Operations
- [ ] **Deployment diagnostics**
- [ ] Vercel diagnostics page
- [ ] Neon diagnostics page
- [ ] Schema version reporting
- [ ] Migration state checks
- [ ] Connection health checks without exposing secrets
- [ ] **Audit logging**
- [ ] Structured audit-log views
- [ ] Token creation events
- [ ] Token revocation events
- [ ] Message append events
- [ ] Session rename events
- [ ] Archive events
- [ ] **Backup and restore**
- [ ] Neon branch backup documentation
- [ ] JSON export restore process
- [ ] Automated backup scripts
- [ ] **Rate limiting and observability**
- [ ] AI-facing append endpoint rate limiting
- [ ] Admin token creation rate limiting
- [ ] Production log guidance with secret redaction requirements
- [ ] Metrics collection setup
- [ ] Alerting configuration
- [ ] **Scaling**
- [ ] Custom domain documentation
- [ ] Preview environment setup
- [ ] Monitoring setup guide
- [ ] Backup and disaster recovery procedures
- [ ] Horizontal scaling guide
## Later Candidates
- [ ] Multiple admin users without external auth
- [ ] Per-user data isolation
- [ ] Role-based access control
- [ ] OAuth/SSO after the single-admin model is no longer sufficient
- [ ] MFA support
- [ ] IP allowlisting
- [ ] Optional app-level encryption
- [ ] Read-only session sharing with topic fields hidden from AI-facing surfaces
- [ ] Import prior AI conversations while preserving immutable message ordinals
- [ ] Richer correction workflows that link correction messages to original message IDs
- [ ] Local SQLite development profile
- [ ] Signed messages
- [ ] Advanced threat detection
## Status Legend
- [x] Completed and tested
- [ ] Planned or not started