From d5c3d7e1c43c41b4ff9737474c054bd77afc4e62 Mon Sep 17 00:00:00 2001 From: TomMass Date: Fri, 20 Mar 2026 01:34:27 +0100 Subject: [PATCH 1/2] Rewrite docs, fix dashboard tooltip, sidebar status refresh, branching guide Docs: - data-model.md: full ER diagram with all current tables, column details, multi-user tables, analytics tables, migration file reference - tool-reference.md: 32 tools (was 31), workflows, token budget guide - scaling.md: reflects current auth/RBAC, WebSocket scaling, production checklist - token-stats-metrics.md: new comprehensive metrics reference Frontend: - Fix sidebar status dot not updating on status change (refresh project data) - Fix tooltip readability on Stats dashboard (solid bg, bold labels, shadow) AGENTS.md: - Add Git Branching Strategy section (feature/fix/docs/refactor branches) --- AGENTS.md | 35 ++ docs/data-model.md | 286 +++++++++++-- docs/scaling.md | 347 ++++++++++++++-- docs/token-stats-metrics.md | 386 ++++++++++++++++++ docs/tool-reference.md | 259 +++++++++--- frontend/src/app/projects/[slug]/page.tsx | 1 + .../src/components/token-stats-dashboard.tsx | 14 +- 7 files changed, 1208 insertions(+), 120 deletions(-) create mode 100644 docs/token-stats-metrics.md diff --git a/AGENTS.md b/AGENTS.md index d83a21c..53614bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -146,6 +146,41 @@ python -m venv .venv && .venv/bin/pip install -r tests/requirements.txt ``` **Important:** Always use `uvx` or a virtual environment for running tests — never install packages into the global Python environment. +## Git Branching Strategy + +Always work on **feature branches**, never commit directly to `main` or `multiuser`. + +### Branch naming + +| Type | Pattern | Example | +|------|---------|---------| +| New feature | `feature/` | `feature/pdf-export` | +| Bug fix | `fix/` | `fix/sidebar-status-dot` | +| Documentation | `docs/` | `docs/token-stats-metrics` | +| Refactor | `refactor/` | `refactor/auth-middleware` | + +### Workflow + +```bash +# 1. Create branch from the current base (usually multiuser or main) +git checkout -b feature/my-feature multiuser + +# 2. Make changes, commit +git add +git commit -m "Description of changes" + +# 3. Push and create PR +git push -u origin feature/my-feature +gh pr create --base multiuser --title "Short title" --body "..." +``` + +### Rules + +- **One branch per logical change.** Don't mix unrelated features in one branch. +- **Base branch:** Use `multiuser` for active development. Use `main` only for release PRs. +- **PR required:** All changes go through pull requests — no direct pushes to `main` or `multiuser`. +- **Delete after merge:** Feature branches are deleted after PR is merged. + ## Critical Rules - **NEVER DROP THE DATABASE.** Do not run `DROP SCHEMA`, `DROP DATABASE`, `DROP TABLE` or any destructive SQL on the PRDforge PostgreSQL database. It contains user project data (sections, revisions, dependencies, comments) that cannot be recreated. For database restores, use `psql < backup.sql` directly — never drop-and-restore. Always ask the user before any destructive database operation. diff --git a/docs/data-model.md b/docs/data-model.md index a565194..7895b2a 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -1,18 +1,46 @@ # Data Model +**Last updated:** 2026-03-20 +**Status:** Current +**Audience:** Contributors, integrators, MCP tool developers + +--- + +## Overview + +PRDforge stores all project data in PostgreSQL. The schema supports multi-user collaboration with role-based access, real-time presence via WebSocket tokens, and session-based token savings tracking. + +## Table of Contents + +- [Entity Relationship Diagram](#entity-relationship-diagram) +- [Core Tables](#core-tables) +- [Multi-User Tables](#multi-user-tables) +- [Analytics Tables](#analytics-tables) +- [Dependency Types](#dependency-types) +- [Section Statuses](#section-statuses) +- [Tags](#tags) +- [Dependency Graph Example](#dependency-graph-example) + +--- + ## Entity Relationship Diagram ```mermaid erDiagram projects ||--o{ sections : has + projects ||--o{ project_members : has + projects ||--o{ section_dependencies : scopes + projects ||--o| project_settings : has + projects ||--o{ token_estimates : tracks + projects ||--o{ section_access_log : tracks + projects ||--o{ mcp_activity : logs + projects ||--o{ audit_events : logs + projects ||--o| project_chats : has sections ||--o{ section_revisions : tracks sections ||--o{ section_dependencies : "from" sections ||--o{ section_dependencies : "to" sections ||--o{ section_comments : has section_comments ||--o{ comment_replies : has - projects ||--o| project_settings : has - projects ||--o{ token_estimates : tracks - projects ||--o| project_chats : has project_chats ||--o{ chat_messages : has sections ||--o| sections : parent @@ -20,17 +48,28 @@ erDiagram uuid id PK text slug UK text name + text description int version + text organization_id + text created_by + timestamptz created_at + timestamptz updated_at } sections { uuid id PK uuid project_id FK text slug + text title text content text summary + text notes text status - text tags + text section_type + jsonb tags int word_count + int sort_order + uuid parent_id FK + text updated_by } section_revisions { uuid id PK @@ -38,6 +77,9 @@ erDiagram int revision_number text content text summary + text change_description + text created_by + timestamptz created_at } section_dependencies { uuid id PK @@ -45,23 +87,37 @@ erDiagram uuid section_id FK uuid depends_on_id FK text dependency_type + text description } section_comments { uuid id PK uuid section_id FK text anchor_text + text anchor_prefix + text anchor_suffix text body boolean resolved + text created_by + timestamptz created_at } comment_replies { uuid id PK uuid comment_id FK text author text body + timestamptz created_at + } + project_members { + uuid id PK + uuid project_id FK + text user_id + text role + timestamptz created_at + timestamptz updated_at } project_settings { uuid project_id PK - json settings + jsonb settings } token_estimates { uuid id PK @@ -69,60 +125,212 @@ erDiagram text operation int full_doc_tokens int loaded_tokens + timestamptz created_at + } + section_access_log { + uuid id PK + uuid project_id FK + uuid section_id FK + text access_level + timestamptz created_at + } + mcp_activity { + uuid id PK + uuid project_id FK + text tool_name + jsonb detail + text user_id + timestamptz created_at + } + audit_events { + uuid id PK + uuid project_id FK + text user_id + text action + text resource + jsonb detail + timestamptz created_at } project_chats { uuid id PK - uuid project_id UK, FK + uuid project_id FK + text chat_type timestamptz created_at - timestamptz updated_at } chat_messages { uuid id PK uuid chat_id FK text role text content - json metadata + jsonb metadata + text created_by + timestamptz created_at + } + prdforge_bootstrap { + uuid id PK + text setup_type UK + boolean completed + timestamptz created_at } ``` +--- + +## Core Tables + +### `projects` + +Top-level container for a PRD. Each project has a unique slug used in URLs and MCP tool calls. + +| Column | Type | Description | +|:-------|:-----|:-----------| +| `id` | UUID | Primary key | +| `slug` | TEXT | Unique URL-safe identifier (e.g., `snaphabit`) | +| `name` | TEXT | Display name | +| `description` | TEXT | Optional project description | +| `version` | INT | Incremented on structural changes | +| `organization_id` | TEXT | Optional org scope (Better Auth) | +| `created_by` | TEXT | Better Auth user ID of creator | + +### `sections` + +Individual PRD sections within a project. Each section has content, metadata, and optional parent for nesting. + +| Column | Type | Description | +|:-------|:-----|:-----------| +| `id` | UUID | Primary key | +| `project_id` | UUID | FK → projects | +| `slug` | TEXT | Unique within project (e.g., `data-model`) | +| `title` | TEXT | Display title | +| `content` | TEXT | Markdown content | +| `summary` | TEXT | Short summary for `prd_get_overview` | +| `notes` | TEXT | Private notes (accordion in UI) | +| `status` | TEXT | Workflow status (see [Section Statuses](#section-statuses)) | +| `section_type` | TEXT | Category (overview, tech_spec, data_model, etc.) | +| `tags` | JSONB | Array of string tags | +| `word_count` | INT | Auto-calculated on content change | +| `sort_order` | INT | Display ordering | +| `parent_id` | UUID | FK → sections (self-referential, for nesting) | + +### `section_revisions` + +Immutable revision history. A new revision is created automatically on every `prd_update_section` that changes content. + +### `section_dependencies` + +Directed edges between sections. See [Dependency Types](#dependency-types) for the type enum. + +### `section_comments` + +Inline comments anchored to selected text within a section. Supports resolve/reopen toggle. + +### `comment_replies` + +Threaded replies on comments. Created by users or auto-generated when a comment is resolved via `prd_update_section(resolve_comments=[...])`. + +--- + +## Multi-User Tables + +### `project_members` + +Maps users to projects with role-based access. User IDs are Better Auth format (32-char random strings, not UUIDs). + +| Column | Type | Description | +|:-------|:-----|:-----------| +| `user_id` | TEXT | Better Auth user ID | +| `role` | TEXT | One of: `owner`, `admin`, `editor`, `commenter`, `viewer` | + +**Role hierarchy:** owner > admin > editor > commenter > viewer. Each role inherits all permissions of lower roles. + +### `prdforge_bootstrap` + +Controls first-user setup flow. When empty (no rows), the system operates in pre-setup mode — all endpoints are open, no auth enforced. + +### `audit_events` + +Logs user actions for project audit trail. Indexed by project and user. + +--- + +## Analytics Tables + +### `token_estimates` + +Per-operation token tracking. Written on every MCP read tool call. Source for the "Savings by Operation" chart and daily trend. + +| Column | Type | Description | +|:-------|:-----|:-----------| +| `operation` | TEXT | MCP tool name (e.g., `read_section`, `get_overview`) | +| `full_doc_tokens` | INT | What full document load would have cost | +| `loaded_tokens` | INT | What was actually loaded | + +### `section_access_log` + +Session-based access tracking with coverage levels. Primary source for the savings gauge. See [Token Stats Metrics](./token-stats-metrics.md) for detailed explanation. + +| Column | Type | Description | +|:-------|:-----|:-----------| +| `access_level` | TEXT | `full`, `summary`, or `snippet` | + +### `mcp_activity` + +Logs all mutating MCP tool calls (12 tools) with detail JSON. Source for the "Write Operations" donut chart. + +--- + ## Dependency Types When linking sections with `prd_add_dependency`, use one of these types: | Type | Meaning | Example | -|------|---------|---------| -| `blocks` | Section cannot proceed until dependency is complete | `pipeline` blocks `api-spec` | -| `extends` | Section builds upon or extends the dependency | `api-spec` extends `data-model` | +|:-----|:--------|:--------| +| `blocks` | Section cannot proceed until dependency is complete | `data-model` blocks `api-spec` | +| `extends` | Section builds upon the dependency | `api-spec` extends `data-model` | | `implements` | Section implements what the dependency specifies | `ui-design` implements `api-spec` | | `references` | Section references the dependency for context (default) | `security` references `tech-stack` | +The dependency graph in the UI uses these types for edge coloring: +- `blocks` — red edges +- `extends` — blue edges +- `implements` — green edges +- `references` — gray edges + +--- + +## Section Statuses + +| Status | Meaning | Dot color | +|:-------|:--------|:----------| +| `draft` | Initial writing, not yet reviewed | Gray | +| `in_progress` | Actively being worked on | Yellow | +| `review` | Ready for review | Blue | +| `approved` | Finalized and approved | Green | +| `outdated` | Needs update due to changes in dependencies | Red | + +Status is set via the dropdown in the section viewer header or via `prd_update_section(status="approved")`. + +--- + ## Tags -Tags categorize sections for filtering and search (via `prd_search(query="tag:mvp")`): +Tags categorize sections for filtering and search. Query with `prd_search(query="tag:mvp")`. | Tag | Purpose | -|-----|---------| +|:----|:--------| | `mvp` | Part of minimum viable product scope | | `core` | Core system functionality | -| `infra` | Infrastructure and deployment concerns | +| `infra` | Infrastructure and deployment | | `ai` | AI/ML related components | -| `frontend` | User-facing interface components | - -Tags are freeform — you can create any tag. The above are conventions used in the seed data. +| `frontend` | User-facing interface | -## Section Statuses +Tags are freeform — create any tag that fits your project. The above are conventions from the SnapHabit seed data. -| Status | Meaning | -|--------|---------| -| `draft` | Initial writing, not yet reviewed | -| `in_progress` | Actively being worked on | -| `review` | Ready for review | -| `approved` | Finalized and approved | -| `outdated` | Needs update due to changes in dependencies | +--- -## Dependency Graph (SnapHabit Example) +## Dependency Graph Example -This graph shows the dependencies in the default seed (`02_seed.sql`): +The default SnapHabit seed data (`db/02_seed.sql`) creates this dependency structure: ```mermaid graph TD @@ -139,3 +347,27 @@ graph TD DM --> TL[timeline] MA --> TL ``` + +--- + +## Migration Files + +Schema is applied via ordered SQL files in `db/`: + +| File | Purpose | +|:-----|:--------| +| `01_init.sql` | Core tables (projects, sections, revisions, dependencies) | +| `02_seed.sql` | SnapHabit seed data (12 sections, 12 dependencies) | +| `03_comments.sql` | Section comments and replies | +| `04_replies_and_settings.sql` | Comment replies, project settings | +| `05_token_stats.sql` | Token estimates tracking | +| `06_chat.sql` | Project chats and messages | +| `07_multi_user.sql` | Project members, bootstrap, bridge columns (TEXT not UUID) | +| `08_mcp_activity.sql` | MCP tool activity logging | +| `09_audit.sql` | Audit events | +| `10_password_reset.sql` | Admin password reset tokens | +| `11_chat_multiuser.sql` | Chat type column, user attribution | +| `12_better_auth.sql` | Better Auth session/user tables | +| `13_section_access_log.sql` | Session-based access tracking | + +All migrations are idempotent (`CREATE TABLE IF NOT EXISTS`, `DO $$ BEGIN ... END $$` guards). diff --git a/docs/scaling.md b/docs/scaling.md index a4b289a..9a9e5ff 100644 --- a/docs/scaling.md +++ b/docs/scaling.md @@ -1,26 +1,58 @@ -# Scaling & Multi-User Considerations +# Scaling & Production Deployment -PRD Forge is designed as a **single-user local tool**. This document describes what you'd need to change if you want to scale it beyond that. +**Last updated:** 2026-03-20 +**Status:** Current +**Audience:** Self-hosters, DevOps engineers, contributors planning production deployment -## Current Configuration +--- -The MCP server uses an asyncpg connection pool with these defaults: +## Overview + +PRDforge ships as a Docker Compose stack designed for single-host deployment. This document covers what to consider when scaling beyond a single user or deploying to production infrastructure. + +## Table of Contents + +- [Current Architecture](#current-architecture) +- [Connection Pooling](#connection-pooling) +- [Authentication & Access Control](#authentication--access-control) +- [Reverse Proxy & TLS](#reverse-proxy--tls) +- [Database Scaling](#database-scaling) +- [WebSocket Scaling](#websocket-scaling) +- [Monitoring & Observability](#monitoring--observability) +- [Backup & Recovery](#backup--recovery) +- [Production Checklist](#production-checklist) + +--- + +## Current Architecture + +PRDforge runs **5 Docker containers** on a single host: + +| Service | Port | Purpose | +|:--------|:-----|:--------| +| `postgres` | 5432 | PostgreSQL 16 — app data + Prefect-style metadata | +| `mcp-server` | 8080 | MCP tool server (Python, FastMCP, Streamable HTTP) | +| `python-api` | 8088 | FastAPI — REST API for dashboard, chat SSE, WebSocket | +| `redis` | — | Token replay protection (WS jti SET NX EX), pub/sub | +| `frontend` | 3000 | Next.js — dashboard UI | + +The asyncpg connection pool defaults: ```python asyncpg.create_pool(DATABASE_URL, min_size=2, max_size=10) ``` -This is appropriate for a single user running Claude against one PostgreSQL instance. +Suitable for 1–5 concurrent users on a single host. + +--- -## Connection Pooling for Multi-User +## Connection Pooling -If multiple users connect concurrently, you'll want external connection pooling: +For higher concurrency (10+ simultaneous users), add external connection pooling: -- **PgBouncer** — lightweight PostgreSQL connection pooler. Place between the MCP server and PostgreSQL. Configure in transaction pooling mode to maximize connection reuse. -- **Increase `max_size`** — bump the asyncpg pool to 20-50 depending on expected concurrency. -- **Monitor connections** — watch `pg_stat_activity` for connection saturation. +### PgBouncer (recommended) -Example PgBouncer setup: +Place between application containers and PostgreSQL. Use transaction pooling mode: ```ini [databases] @@ -30,52 +62,297 @@ prdforge = host=postgres port=5432 dbname=prdforge pool_mode = transaction max_client_conn = 200 default_pool_size = 20 +min_pool_size = 5 +``` + +### Application-Level Tuning + +Increase the asyncpg pool size in both the MCP server and Python API: + +```python +# For 10-20 concurrent users +asyncpg.create_pool(DATABASE_URL, min_size=5, max_size=30) ``` -## Authentication & Reverse Proxy +### Monitoring + +Watch for connection saturation: + +```sql +SELECT count(*), state FROM pg_stat_activity +WHERE datname = 'prdforge' +GROUP BY state; +``` + +--- + +## Authentication & Access Control + +PRDforge includes **built-in authentication** via Better Auth: + +| Feature | Status | +|:--------|:-------| +| Email/password authentication | Implemented | +| Closed sign-up (admin creates users) | Implemented | +| First-user bootstrap flow | Implemented | +| Password reset (admin-generated tokens) | Implemented | +| RBAC (5 roles: owner → viewer) | Implemented | +| Session-based auth on all endpoints | Implemented | +| Google OAuth | Config ready, needs Cloud Console credentials | + +### Pre-Setup Mode + +When no users exist (bootstrap table empty), all endpoints are open — no auth enforced. This allows initial setup via MCP tools or the UI. After the first user is created via `/api/auth/setup`, authentication is enforced on all protected endpoints. + +### WebSocket Authentication + +WebSocket connections use short-lived HMAC tokens: +1. Client calls `POST /api/ws-token` (authenticated, user_id derived from session) +2. Server mints a token with 120-second TTL +3. Client connects to `ws://host/ws/projects/{slug}?token=...` +4. Server verifies HMAC, checks jti uniqueness via Redis (replay protection) +5. Server re-checks project membership before accepting + +--- -PRD Forge has **no built-in authentication**. For multi-user or non-localhost deployments: +## Reverse Proxy & TLS -1. **Reverse proxy** — put nginx, Caddy, or Traefik in front of the MCP server (port 8080) and Web UI (port 8088) -2. **TLS termination** — configure HTTPS at the proxy level -3. **Authentication** — add HTTP Basic Auth, OAuth2 proxy (e.g., oauth2-proxy), or mTLS at the proxy layer -4. **Network isolation** — restrict the MCP server to only accept connections from the proxy, not directly from clients +For non-localhost deployment, place a reverse proxy in front of all services: -Example nginx config snippet: +### Nginx Example ```nginx server { - listen 443 ssl; - server_name prdforge.internal; + listen 443 ssl http2; + server_name prdforge.example.com; ssl_certificate /etc/ssl/prdforge.crt; ssl_certificate_key /etc/ssl/prdforge.key; - # Basic auth - auth_basic "PRD Forge"; - auth_basic_user_file /etc/nginx/.htpasswd; + # Frontend (Next.js) + location / { + proxy_pass http://127.0.0.1:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } - location /mcp/ { - proxy_pass http://127.0.0.1:8080; + # API + location /api/ { + proxy_pass http://127.0.0.1:8088; + proxy_set_header Host $host; } - location / { + # WebSocket + location /ws/ { proxy_pass http://127.0.0.1:8088; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400s; + } + + # MCP Server (restrict to internal/VPN) + location /mcp/ { + proxy_pass http://127.0.0.1:8080; + allow 10.0.0.0/8; + allow 172.16.0.0/12; + deny all; } } ``` +**Key considerations:** +- WebSocket endpoints need `Upgrade` and `Connection` headers +- MCP server should be restricted to trusted networks (Claude Code/Desktop connects directly) +- Set `proxy_read_timeout` high for WebSocket connections and SSE chat streams + +--- + ## Database Scaling -- **Read replicas** — for read-heavy workloads, configure PostgreSQL streaming replication and point read-only MCP tools to a replica -- **Backup strategy** — already documented in the README (`pg_dump` to file or MinIO) -- **Vacuuming** — with heavy `token_estimates` inserts, ensure autovacuum is tuned. Consider periodic `DELETE FROM token_estimates WHERE created_at < now() - interval '90 days'` to prune old data. +### Read Replicas + +For read-heavy workloads, configure PostgreSQL streaming replication and route read-only MCP tools to a replica: + +```python +# Read pool (replica) +read_pool = asyncpg.create_pool(REPLICA_URL, min_size=2, max_size=20) + +# Write pool (primary) +write_pool = asyncpg.create_pool(PRIMARY_URL, min_size=2, max_size=10) +``` + +### Vacuuming + +The `token_estimates` and `section_access_log` tables grow continuously. Ensure autovacuum is tuned: + +```sql +-- Check autovacuum stats +SELECT relname, n_dead_tup, last_autovacuum +FROM pg_stat_user_tables +WHERE schemaname = 'public' +ORDER BY n_dead_tup DESC; +``` + +Consider periodic pruning for analytics tables: + +```sql +-- Keep 90 days of token estimates +DELETE FROM token_estimates WHERE created_at < now() - interval '90 days'; + +-- Keep 90 days of access logs +DELETE FROM section_access_log WHERE created_at < now() - interval '90 days'; +``` + +### Indexes + +All critical indexes are created in the migration files. Verify they exist: + +```sql +SELECT indexname, tablename FROM pg_indexes +WHERE schemaname = 'public' +ORDER BY tablename; +``` + +--- + +## WebSocket Scaling + +### Single Host (Current) + +WebSocket connections are held in-memory by the Python API process. Presence broadcasts use a local dict. Redis pub/sub is used for cross-process event broadcasting. + +### Multi-Process / Multi-Host + +If running multiple API instances behind a load balancer: + +1. **Sticky sessions** — required for WebSocket connections. Configure your load balancer to route WebSocket upgrades to the same backend. +2. **Redis pub/sub** — already implemented. All project events are published to Redis channels. Multiple API instances subscribe and broadcast to their local connections. +3. **Token replay protection** — already uses Redis `SET NX EX`. Works across multiple instances. + +--- + +## Monitoring & Observability + +### Built-in + +| What | Where | +|:-----|:------| +| Pipeline health | Stats tab in the dashboard | +| Token savings | Stats tab — gauge, charts, by-operation breakdown | +| MCP tool activity | `mcp_activity` table (last 50 entries in Stats tab) | +| Audit trail | `audit_events` table, `/api/projects/{slug}/audit` endpoint | + +### External Integration (Optional) + +| Tool | Integration point | +|:-----|:-----------------| +| **Prometheus** | Add `/metrics` endpoint (TODO — see `TODO.md`) | +| **Grafana** | Connect to PostgreSQL directly for custom dashboards | +| **Structured logging** | Python API and MCP server use Python `logging` — configure JSON formatter for log aggregation | + +### Health Checks + +All services expose health checks used by Docker Compose: + +```bash +# PostgreSQL +pg_isready -U prdforge + +# MCP Server +curl -sf http://localhost:8080/mcp/ + +# Python API +curl -sf http://localhost:8088/health + +# Redis +redis-cli ping + +# Frontend +curl -sf http://localhost:3000/ +``` + +--- + +## Backup & Recovery + +### Database Backup + +```bash +# Dump to file +docker compose exec postgres pg_dump -U prdforge prdforge > backup_$(date +%Y%m%d).sql + +# Restore from file +docker compose exec -i postgres psql -U prdforge prdforge < backup_20260320.sql +``` + +### Volume Backup + +PostgreSQL data lives in the `pgdata` Docker volume: + +```bash +# Find volume location +docker volume inspect prdforge_pgdata --format '{{ .Mountpoint }}' + +# Backup volume (stop services first) +docker compose stop +tar czf pgdata_backup.tar.gz -C /var/lib/docker/volumes/prdforge_pgdata/_data . +docker compose up -d +``` + +### Recovery Testing + +Periodically verify backups can be restored: + +```bash +# Create a test database +docker compose exec postgres createdb -U prdforge prdforge_test + +# Restore backup into test database +docker compose exec -i postgres psql -U prdforge prdforge_test < backup.sql + +# Verify +docker compose exec postgres psql -U prdforge prdforge_test -c "SELECT count(*) FROM projects;" + +# Cleanup +docker compose exec postgres dropdb -U prdforge prdforge_test +``` + +--- + +## Production Checklist + +Before deploying to production, verify: + +### Security + +- [ ] `WS_TOKEN_SECRET` env var set (not using default dev value) +- [ ] `BETTER_AUTH_SECRET` env var set +- [ ] PostgreSQL password changed from default (`prdforge`) +- [ ] MCP server port (8080) not exposed to public internet +- [ ] TLS configured via reverse proxy +- [ ] First user created via bootstrap flow + +### Reliability + +- [ ] Docker restart policies set to `unless-stopped` (default in compose) +- [ ] PostgreSQL health check passing +- [ ] Redis health check passing +- [ ] Log rotation configured for Docker containers +- [ ] Database backup schedule in place + +### Performance -## What PRD Forge Does NOT Support (Yet) +- [ ] asyncpg pool size tuned for expected concurrency +- [ ] PgBouncer deployed if >10 concurrent users expected +- [ ] Autovacuum verified on analytics tables +- [ ] Redis maxmemory configured if running on constrained host -- Multi-tenant isolation (all data is in one schema) -- Per-user permissions or RBAC -- Session management or API keys -- Rate limiting +### Observability -These would need to be built if you're deploying PRD Forge as a shared service. +- [ ] Health check endpoints monitored +- [ ] Database connection count monitored +- [ ] Disk space monitored (PostgreSQL data volume) +- [ ] Log aggregation configured (optional) diff --git a/docs/token-stats-metrics.md b/docs/token-stats-metrics.md new file mode 100644 index 0000000..d2de7c3 --- /dev/null +++ b/docs/token-stats-metrics.md @@ -0,0 +1,386 @@ +# Token Stats Dashboard + +**Last updated:** 2026-03-20 +**Status:** Current +**Audience:** PRDforge users, contributors, and integrators + +--- + +## Overview + +The Stats tab measures how efficiently MCP tools consume LLM context window tokens when interacting with a PRD project. Rather than loading the entire document into context on every tool call, PRDforge serves targeted slices — individual sections, summaries, or metadata — and tracks the resulting savings. + +This document explains every metric, chart, and data source visible on the Stats dashboard. + +## Table of Contents + +- [Core Concepts](#core-concepts) +- [Dashboard Layout](#dashboard-layout) + - [Top-Level Stat Cards](#top-level-stat-cards) + - [7-Day Token Savings](#7-day-token-savings) + - [Full Doc vs Loaded](#full-doc-vs-loaded) + - [Savings by Operation](#savings-by-operation) + - [Write Operations](#write-operations) + - [Section Heatmap](#section-heatmap) +- [Calculation Methodology](#calculation-methodology) +- [Data Sources](#data-sources) +- [API Response Schema](#api-response-schema) +- [Interpreting Your Dashboard](#interpreting-your-dashboard) +- [Troubleshooting](#troubleshooting) + +--- + +## Core Concepts + +### Token Estimation + +PRDforge converts word counts to token estimates using a **1.3× multiplier** (1 word ≈ 1.3 tokens). This approximation is consistent with typical English-language tokenization across Claude models. + +``` +tokens = word_count × 1.3 +``` + +### Access Levels + +Every section read is tagged with an access level that determines its **coverage fraction** — the proportion of the section's content actually delivered to the agent: + +| Access Level | Coverage | Triggered by | Example | +|:------------|:---------|:------------|:--------| +| `full` | 100% | `prd_read_section` | Agent reads complete section content for editing | +| `summary` | 10% | `prd_get_overview` | Agent needs a quick overview of all sections | +| `snippet` | 15% | `prd_list_sections` | Agent checks section titles, statuses, and tags | + +### Session Windowing + +Tool calls are grouped into **sessions** using a 30-minute inactivity window: + +``` +Call at 10:00 ──┐ +Call at 10:05 │ Session 1 +Call at 10:22 ──┘ + ← 35 min gap (> 30 min) → +Call at 10:57 ──┐ +Call at 11:03 │ Session 2 +Call at 11:10 ──┘ +``` + +**Deduplication rule:** Within a session, if the same section is accessed multiple times, only the **highest** access level counts. Reading a section summary and then reading it in full counts as one full read, not a summary + a full read. + +### Savings Formula + +Per-session savings percentage: + +``` +savings_pct = (1 - unique_loaded_words / full_doc_words) × 100 +``` + +Where: +- `full_doc_words` = total word count across all sections in the project +- `unique_loaded_words` = sum of `section_word_count × coverage_fraction` for each section touched in the session, deduplicated by highest access level + +--- + +## Dashboard Layout + +### Top-Level Stat Cards + +Five cards displayed across the top of the dashboard: + +#### 1. Savings Gauge (ring chart) + +Average token savings percentage across all sessions. + +| Ring Color | Range | Meaning | +|:----------|:------|:--------| +| Green | > 80% | Excellent — agents rely on summaries and targeted reads | +| Yellow | 50–80% | Moderate — some full-document loads occurring | +| Red | < 50% | Low — agents are loading most of the document per session | + +A new project typically starts at 0% and improves as usage patterns shift toward `prd_get_overview` and `prd_list_sections` instead of reading every section in full. + +#### 2. Sessions + +Total number of distinct interaction sessions. The subtitle shows average sections touched per session (e.g., `12.0 sections/session`). + +When session-based tracking has no data (legacy mode), this card falls back to showing total operation count. + +#### 3. Best Session + +Highest savings percentage achieved in any single session. Represents the best-case efficiency your workflow has produced so far. + +#### 4. Full Document + +Total token count of the entire PRD if loaded all at once. + +``` +full_doc_tokens = SUM(sections.word_count) × 1.3 +``` + +Subtitle shows the number of sections in the project. + +#### 5. Project + +Structural stats for the project: +- **Dependencies** — total dependency links between sections +- **Revisions** — total revision count across all sections + +--- + +### 7-Day Token Savings + +**Chart type:** Area chart with gradient fill +**Color:** Green (`#22c55e`) + +Displays daily total tokens saved over the last 7 calendar days. + +**Data point formula:** +```sql +tokens_saved = SUM(full_doc_tokens - loaded_tokens) +-- per day, from token_estimates table +``` + +**What to look for:** +- **Rising trend** — agents are becoming more efficient (using summaries, targeted reads) +- **Spikes** — bulk operations (e.g., creating 12 sections via template) generate large savings in a single day +- **Flat at zero** — no MCP tool usage in the time period, or every call loaded full content + +--- + +### Full Doc vs Loaded + +**Chart type:** Custom vertical bar chart with three columns + +| Column | Color | Meaning | +|:-------|:------|:--------| +| **Full Doc** | Red (`#ef4444`) | What would have been consumed if the entire PRD was loaded into context every session | +| **Loaded** | Green (`#22c55e`) | Tokens actually delivered to agents across all sessions | +| **Saved** | Dashed purple outline (`#6366f1`) | Difference: `Full Doc − Loaded` | + +**Calculation:** +``` +Full Doc = full_doc_tokens × session_count +Loaded = total_loaded_tokens (sum of unique loaded words × 1.3 across all sessions) +Saved = Full Doc − Loaded +``` + +**When Saved = 0:** Every section was read in full during every session. Normal for: +- Newly created projects (agent just built all sections) +- Comprehensive review workflows (agent intentionally reads everything) +- Projects with very few sections where the overhead is negligible + +--- + +### Savings by Operation + +**Chart type:** Horizontal stacked bar chart +**Location:** Bottom-left, spans 2 columns + +Breaks down token savings per MCP tool type. Each operation gets a bar with two segments: + +| Segment | Color | Meaning | +|:--------|:------|:--------| +| Colored | Per-operation color | Tokens loaded by this operation across all calls | +| Gray | `var(--surface)`, 40% opacity | Tokens saved (full doc size minus loaded, per call) | + +**Common operations and their savings profile:** + +| Operation | Description | Typical loaded | Savings characteristic | +|:----------|:-----------|:--------------|:----------------------| +| `read section` | Loads one section's full content | ~1K tokens | High — loads 1 section instead of 12+ | +| `get overview` | Returns all section summaries (no content) | ~200 tokens | Very high — lightweight summaries only | +| `list sections` | Returns metadata (titles, status, tags, word count) | ~150 tokens | Very high — no content at all | +| `read revision` | Loads a specific historical revision | ~1K tokens | Similar to read section | +| `search` | Full-text search, returns matching snippets | Variable | High — only matching fragments returned | + +**Tooltip displays:** Operation name, Tokens Loaded (cumulative), Tokens Saved (cumulative). + +**How savings compound:** Each call to `read section` saves `full_doc_tokens − section_tokens`. With a 13K-token document and 26 read calls averaging 500 tokens each, total savings = `26 × (13,000 − 500) = 325K tokens saved`. + +--- + +### Write Operations + +**Chart type:** Donut chart +**Location:** Bottom-right + +Distribution of mutating MCP tool calls logged in `mcp_activity`. Shows how the project was modified, not read. + +**Operations tracked:** + +| Operation | Description | +|:----------|:-----------| +| `update_section` | Edit section content (creates a revision) | +| `create_section` | Add a new section to the project | +| `add_dependency` | Create a dependency link between two sections | +| `remove_dependency` | Remove a dependency link | +| `move_section` | Change section sort order | +| `duplicate_section` | Clone a section | +| `delete_section` | Remove a section | +| `update_settings` | Modify project settings | + +**Legend:** Displays below the donut with operation name and count. The header shows total write operation count. + +**Interpreting the ratio:** Initial PRD creation is write-heavy (many `create_section` + `add_dependency`). Ongoing refinement shifts toward `update_section`. A healthy mature project shows mostly updates with occasional creates. + +--- + +### Section Heatmap + +**Condition:** Only displayed when `section_access_log` has data for the project. + +Shows which sections are accessed most frequently, helping identify: +- **Hot sections** — frequently read, may benefit from better summaries +- **Cold sections** — rarely accessed, may be candidates for archiving or merging +- **Full-read sections** — sections that are always read in full (no summary suffices) + +--- + +## Calculation Methodology + +### Session-Based (Primary) + +Used when `section_access_log` has data for the project. This is the "honest" calculation. + +```sql +-- 1. Identify session boundaries (30-min gap) +-- 2. Per session, per section: take MAX access level +-- 3. Per session: sum(section_word_count × coverage_fraction) +-- 4. Per session: savings_pct = (1 - loaded / full_doc) × 100 +-- 5. Aggregate: avg(savings_pct) across sessions +``` + +Coverage fractions: `full = 1.0`, `summary = 0.10`, `snippet = 0.15` + +### Legacy Fallback + +Used when `section_access_log` is empty (older data before the access log was added). + +```sql +-- Simple per-operation calculation from token_estimates table +savings = SUM(full_doc_tokens) - SUM(loaded_tokens) +savings_pct = savings / SUM(full_doc_tokens) × 100 +``` + +Both systems are populated in parallel — the access log provides the accurate session-based view, while `token_estimates` provides the per-operation breakdown used by the "Savings by Operation" chart. + +--- + +## Data Sources + +| Table | Schema | Purpose | +|:------|:-------|:--------| +| `section_access_log` | `project_id`, `section_id`, `access_level`, `created_at` | Session-based tracking with access levels. Primary source for savings gauge, sessions, and heatmap. | +| `token_estimates` | `project_id`, `operation`, `full_doc_tokens`, `loaded_tokens`, `created_at` | Per-operation token tracking. Source for daily trend, by-operation breakdown, and legacy fallback. | +| `mcp_activity` | `project_id`, `tool_name`, `detail` (JSONB), `created_at` | Logs all mutating MCP tools. Source for write operations donut chart. Up to 50 most recent entries returned. | +| `sections` | `project_id`, `word_count`, ... | Current word counts for full document size calculation. | +| `section_dependencies` | `project_id`, ... | Dependency count for the Project stat card. | +| `section_revisions` | `section_id`, ... | Revision count for the Project stat card. | + +--- + +## API Response Schema + +**Endpoint:** `GET /api/projects/{slug}/token-stats` + +```typescript +interface TokenStats { + operations: number; // Total MCP read operations + total_full_doc_tokens: number; // Full PRD token count (current) + total_loaded_tokens: number; // Tokens actually loaded across all sessions + total_saved_tokens: number; // Tokens saved (full × sessions − loaded) + savings_percent: number; // Average savings % across sessions + sessions: number; // Distinct session count + best_session_savings: number; // Highest savings % in any session + avg_sections_per_session: number;// Average sections touched per session + + by_operation: { + operation: string; // MCP tool name (e.g., "read_section") + count: number; // Times called + full_tokens: number; // Cumulative full-doc tokens + loaded_tokens: number; // Cumulative loaded tokens + }[]; + + daily_trend: { + day: string; // ISO date (YYYY-MM-DD) + operations: number; // Operations that day + tokens_saved: number; // Tokens saved that day + }[]; + + project_stats: { + sections: number; // Section count + dependencies: number; // Dependency count + revisions: number; // Total revisions + }; + + activity: { + tool_name: string; // e.g., "prd_update_section" + detail: Record; + created_at: string; // ISO timestamp + }[]; + + section_heatmap: { + slug: string; // Section slug + title: string; // Section title + access_count: number; // Times accessed + has_full_read: number; // 1 if ever fully read, 0 otherwise + }[]; +} +``` + +--- + +## Interpreting Your Dashboard + +### Scenario: New project, just created + +| Metric | Expected value | Why | +|:-------|:--------------|:----| +| Savings gauge | 0% | Every section was just created and read in full | +| Full Doc vs Loaded | Equal bars, Saved = 0 | All content was loaded during creation | +| Savings by Operation | `create_section` dominates | Project is being built | +| Write Operations | `create_section`, `add_dependency` | Initial structure | + +### Scenario: Mature project, daily agent usage + +| Metric | Expected value | Why | +|:-------|:--------------|:----| +| Savings gauge | 70–90% | Agents use `get_overview` and `list_sections` for navigation | +| Full Doc vs Loaded | Loaded much smaller than Full Doc | Targeted reads dominate | +| Savings by Operation | `get overview` and `list sections` show highest savings | Lightweight operations most frequent | +| Write Operations | `update_section` dominates | Iterative refinement | + +### Scenario: Agent doing comprehensive review + +| Metric | Expected value | Why | +|:-------|:--------------|:----| +| Savings gauge | 10–30% | Agent reads most sections in full | +| Best Session | May still show high % from prior sessions | Historical best preserved | +| 7-Day Trend | Spike on review day, otherwise normal | One-off comprehensive read | + +--- + +## Troubleshooting + +### Savings always showing 0% + +**Cause:** All MCP calls are `prd_read_section` (full reads). No `get_overview` or `list_sections` usage. + +**Fix:** Encourage agents to use `prd_get_overview` for navigation and `prd_list_sections` for status checks. The AGENTS.md file instructs Claude to prefer lightweight tools. + +### No data on the dashboard + +**Cause:** No MCP tools have been called for this project yet. The `token_estimates` and `section_access_log` tables are empty. + +**Fix:** Use any MCP tool (e.g., `prd_list_sections`) to generate initial data. Stats appear after the first tool call. + +### Daily trend shows data but gauge shows 0% + +**Cause:** Session-based tracking (`section_access_log`) has no data, but legacy `token_estimates` does. The gauge uses session-based math; the trend uses per-operation math. + +**Fix:** This resolves naturally as new MCP calls populate the access log. Both tables are written to in parallel. + +### Write operations count seems too high + +**Cause:** The donut chart counts individual tool calls, not unique changes. An agent that creates 12 sections and 15 dependencies in one conversation generates 27 write operations. + +**Expected behavior:** This is correct. The chart shows agent activity volume, not content change count. diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 5d8ecb5..7c0a418 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -1,94 +1,241 @@ # MCP Tool Reference -PRD Forge exposes 31 MCP tools for Claude to read, write, search, and manage your PRD sections. +**Last updated:** 2026-03-20 +**Status:** Current +**Audience:** Claude agents, MCP integrators, contributors -## Tool Table +--- + +## Overview + +PRDforge exposes **32 MCP tools** for reading, writing, searching, and managing PRD sections. Tools are designed to minimize context window usage — prefer lightweight tools (`prd_list_sections`, `prd_get_overview`) for navigation and reserve full reads (`prd_read_section`) for editing. + +## Table of Contents + +- [Tool Index](#tool-index) +- [Workflows](#workflows) +- [Token Budget Guide](#token-budget-guide) +- [Tool Details by Group](#tool-details-by-group) + +--- + +## Tool Index | Tool | Group | Description | ~Tokens | -|------|-------|-------------|---------| -| `prd_list_projects` | Project | List all projects with stats | 50 | -| `prd_create_project` | Project | Create new project | — | -| `prd_delete_project` | Project | Delete project (cascades) | — | -| `prd_list_sections` | Section | List sections (metadata only) | 200 | -| `prd_read_section` | Section | Read section + dependency context | 500-3000 | -| `prd_create_section` | Section | Create new section | — | -| `prd_update_section` | Section | Update fields, auto-revision on content change | — | -| `prd_delete_section` | Section | Delete section (warns about deps) | — | -| `prd_move_section` | Section | Change sort_order or parent | — | +|:-----|:------|:-----------|:--------| +| `prd_list_projects` | Project | List all projects with section counts | 50 | +| `prd_create_project` | Project | Create project (optional template: `saas-mvp`, `mobile-app`) | — | +| `prd_delete_project` | Project | Delete project and all sections (cascades) | — | +| `prd_list_sections` | Section | List sections — metadata only, no content | 200 | +| `prd_read_section` | Section | Read full section content + dependency context summaries | 500–3000 | +| `prd_create_section` | Section | Create new section with content, tags, type | — | +| `prd_update_section` | Section | Update fields, auto-revision on content change, atomic comment resolve | — | +| `prd_delete_section` | Section | Delete section (warns about dependencies) | — | +| `prd_move_section` | Section | Change sort_order or parent section | — | | `prd_duplicate_section` | Section | Copy section with new slug | — | -| `prd_add_dependency` | Deps | Add/update dependency (idempotent) | — | -| `prd_remove_dependency` | Deps | Remove dependency | — | -| `prd_suggest_dependencies` | Deps | Auto-suggest deps via content similarity | 200 | -| `prd_get_overview` | Context | Project overview with summaries | 400 | -| `prd_search` | Context | Full-text or tag search | 200 | -| `prd_get_changelog` | Context | Recent revision history | 300 | -| `prd_token_stats` | Context | Token savings statistics per project | 200 | -| `prd_get_revisions` | Revision | List revision metadata | 100 | -| `prd_read_revision` | Revision | Read revision content | 500-3000 | -| `prd_rollback_section` | Revision | Rollback with backup | — | -| `prd_export_markdown` | Export | Full document as markdown | 15000+ | -| `prd_import_markdown` | Import | Import from markdown (configurable heading level or manual delimiter) | — | -| `prd_bulk_status` | Batch | Update status for multiple sections | — | -| `prd_list_comments` | Comments | List all comments across project with section pointers | 100-500 | +| `prd_add_dependency` | Deps | Add/update dependency link (idempotent) | — | +| `prd_remove_dependency` | Deps | Remove a dependency link | — | +| `prd_suggest_dependencies` | Deps | Auto-suggest deps via content similarity (TF-IDF) | 200 | +| `prd_list_comments` | Comments | List all comments across project with section pointers | 100–500 | | `prd_add_comment` | Comments | Add inline comment anchored to selected text | — | -| `prd_resolve_comment` | Comments | Resolve/reopen a comment after implementing changes | — | +| `prd_resolve_comment` | Comments | Resolve or reopen a comment | — | | `prd_delete_comment` | Comments | Delete a comment | — | | `prd_add_comment_reply` | Replies | Add a reply to an inline comment | — | | `prd_get_settings` | Settings | Get project settings (merged defaults + overrides) | 50 | | `prd_update_settings` | Settings | Update project settings | — | +| `prd_get_overview` | Context | Project overview with section summaries (~10% of full doc) | 400 | +| `prd_search` | Context | Full-text or tag search across sections | 200 | +| `prd_get_changelog` | Context | Recent revision history across all sections | 300 | +| `prd_token_stats` | Context | Token savings statistics for the project | 200 | +| `prd_get_revisions` | Revision | List revision metadata for a section | 100 | +| `prd_read_revision` | Revision | Read a specific historical revision's content | 500–3000 | +| `prd_rollback_section` | Revision | Rollback to a previous revision (current saved as backup) | — | +| `prd_export_markdown` | Export | Export full document as assembled markdown | 15000+ | +| `prd_import_markdown` | Import | Import from markdown (configurable heading level or delimiter) | — | +| `prd_bulk_status` | Batch | Update status for multiple sections at once | — | + +**Read tools** (return data, consume tokens): 14 tools +**Write tools** (mutate data, logged to `mcp_activity`): 12 tools +**Hybrid tools** (read + compute): 6 tools (search, suggest, stats, changelog, export, import) + +--- -## Usage Examples +## Workflows + +### Standard Editing + +Navigate → Read → Edit → Verify: -**Standard editing workflow:** ``` -prd_get_overview(project="my-project") → TOC + summaries (~400 tokens) -prd_read_section(project="my-project", section="architecture") → full content + dep summaries -prd_update_section(project="my-project", section="architecture", - content="...updated...", change_description="Added caching layer") +prd_get_overview(project="my-project") + → Section list with summaries (~400 tokens) + +prd_read_section(project="my-project", section="architecture") + → Full content + dependency context summaries (~1500 tokens) + +prd_update_section( + project="my-project", + section="architecture", + content="...updated...", + change_description="Added caching layer" +) + → Content saved, revision created automatically ``` -**Impact analysis:** +### Impact Analysis + +Read a section to see what depends on it, then update dependents: + ``` -prd_read_section(section="requirements") → see depended_by list -# Then update each dependent section in order +prd_read_section(section="requirements") + → Response includes depended_by list with summaries + +# Update each dependent section that needs changes +prd_update_section(section="api-spec", content="...", change_description="Aligned with updated requirements") ``` -**Comment-driven editing (auto-resolve):** +### Comment-Driven Editing + +Read comments, make changes, resolve comments atomically: + ``` prd_read_section(project="my-project", section="requirements") - → content + 2 open comments with IDs + replies -prd_update_section(project="my-project", section="requirements", - content="...updated...", change_description="Addressed review feedback", - resolve_comments=["comment-id-1", "comment-id-2"]) - → atomically updates content + resolves comments + auto-replies if setting enabled + → Content + open comments with IDs and replies + +prd_update_section( + project="my-project", + section="requirements", + content="...updated addressing feedback...", + change_description="Addressed review feedback", + resolve_comments=["comment-id-1", "comment-id-2"] +) + → Atomically: updates content + resolves comments + auto-replies ``` -**Rollback:** +### Rollback + +View history, restore a previous version: + ``` -prd_get_revisions(section="architecture") → see revision history -prd_rollback_section(section="architecture", revision=3) → restore, current saved as backup +prd_get_revisions(section="architecture") + → Revision list with numbers, dates, descriptions + +prd_read_revision(section="architecture", revision=3) + → Content of revision 3 + +prd_rollback_section(section="architecture", revision=3) + → Restores revision 3, current content saved as new revision (backup) ``` -**Import with custom heading level:** +### Import + +Import markdown from external sources: + ``` +# Split on ## headings (default) +prd_import_markdown(project="my-project", markdown="## Section One\n...") + +# Split on ### headings prd_import_markdown(project="my-project", markdown="...", heading_level=3) - → splits on ### headings instead of ## + +# Split on custom delimiter +prd_import_markdown(project="my-project", markdown="...", manual_delimiter="") ``` -**Import with manual delimiter:** +### Dependency Suggestions + +Find related sections automatically: + ``` -prd_import_markdown(project="my-project", markdown="...", manual_delimiter="") - → splits on markers, extracts title from first heading in each chunk +prd_suggest_dependencies(project="my-project", section="architecture") + → Top 5 sections with overlapping content, ranked by TF-IDF similarity ``` -**Token savings:** +--- + +## Token Budget Guide + +Context window is finite. Use the lightest tool that gets the job done: + +| Task | Best tool | Tokens | Avoid | +|:-----|:----------|:-------|:------| +| "What sections exist?" | `prd_list_sections` | ~200 | `prd_get_overview` (4× more) | +| "What's the project about?" | `prd_get_overview` | ~400 | `prd_read_section` on each (10×) | +| "What does this section say?" | `prd_read_section` | ~1500 | `prd_export_markdown` (all sections) | +| "What changed recently?" | `prd_get_changelog` | ~300 | Reading each section's revisions | +| "Find sections about auth" | `prd_search` | ~200 | Reading every section | +| "What's the full document?" | `prd_export_markdown` | ~15000 | Only when needed for export | + +### Cost hierarchy (cheapest → most expensive) + ``` -prd_token_stats(project="my-project") - → total operations, tokens saved, savings %, daily trend +prd_list_sections ~200 tokens ← metadata only +prd_get_revisions ~100 tokens ← revision metadata +prd_search ~200 tokens ← matching snippets +prd_get_changelog ~300 tokens ← recent changes +prd_get_overview ~400 tokens ← all summaries +prd_read_section ~1500 tokens ← one section + dep context +prd_export_markdown ~15000 tokens ← everything ``` -**Dependency suggestions:** +--- + +## Tool Details by Group + +### Project Tools + +**`prd_create_project`** accepts an optional `template` parameter: + +| Template | Sections created | Description | +|:---------|:----------------|:-----------| +| *(blank)* | 0 | Empty project | +| `saas-mvp` | 7 | SaaS MVP PRD (overview, tech stack, data model, API, UI, security, timeline) | +| `mobile-app` | 5 | Mobile app PRD (overview, features, UI, data model, deployment) | + +### Section Tools + +**`prd_update_section`** is the most versatile tool. Only provided fields are updated: + ``` -prd_suggest_dependencies(project="my-project", section="architecture") - → top 5 sections with overlapping content, ranked by relevance +# Update just status +prd_update_section(section="auth", status="approved") + +# Update content with revision +prd_update_section(section="auth", content="...", change_description="Added OAuth flow") + +# Update content AND resolve comments atomically +prd_update_section(section="auth", content="...", resolve_comments=["id1", "id2"]) ``` + +If `content` is provided, the current content is saved as a revision **before** the update (atomic transaction). + +### Dependency Tools + +**`prd_add_dependency`** is idempotent — calling it twice with the same arguments is safe (upserts on conflict). + +Dependency types: `blocks`, `extends`, `implements`, `references` (default). + +See [Data Model — Dependency Types](./data-model.md#dependency-types) for full descriptions. + +### Comment Tools + +**`prd_list_comments`** is context-efficient: returns all project comments with section pointers in ~100-500 tokens, avoiding the need to read each section individually. + +Comments support: +- **Text anchoring** — `anchor_text`, `anchor_prefix`, `anchor_suffix` for precise positioning +- **Resolve/reopen** — toggle via `prd_resolve_comment(reopen=True)` +- **Threaded replies** — via `prd_add_comment_reply` +- **Atomic resolve** — resolve comments in the same transaction as content update + +### Settings Tools + +Project settings are stored as JSONB with sensible defaults. `prd_get_settings` returns the merged result (defaults + overrides). + +Key settings: + +| Setting | Default | Description | +|:--------|:--------|:-----------| +| `chat_enabled` | `false` | Enable/disable chat panel | +| `chat_provider` | `claude_cli` | Chat provider: `claude_cli` or `anthropic_api` | +| `chat_model` | `sonnet` | Model for chat | +| `auto_reply_on_resolve` | `true` | Auto-generate reply when resolving comments | diff --git a/frontend/src/app/projects/[slug]/page.tsx b/frontend/src/app/projects/[slug]/page.tsx index 4811b9b..e85dbf0 100644 --- a/frontend/src/app/projects/[slug]/page.tsx +++ b/frontend/src/app/projects/[slug]/page.tsx @@ -618,6 +618,7 @@ export default function ProjectDetailPage() { onCommentAdded={() => { if (activeSection) handleSectionSelect(activeSection.section.slug); + fetchProject(slug).then(setProject).catch(() => {}); }} onTextSelected={handleSelectionContext} /> diff --git a/frontend/src/components/token-stats-dashboard.tsx b/frontend/src/components/token-stats-dashboard.tsx index 48a8f61..4e70dcf 100644 --- a/frontend/src/components/token-stats-dashboard.tsx +++ b/frontend/src/components/token-stats-dashboard.tsx @@ -62,10 +62,20 @@ const tooltipStyle = { background: "var(--card-bg)", border: "1px solid var(--border-color)", borderRadius: 8, - fontSize: 12, + fontSize: 13, color: "var(--fg)", + boxShadow: "0 4px 12px rgba(0,0,0,0.4)", + padding: "10px 14px", + }, + labelStyle: { + color: "var(--fg)", + fontWeight: 600, + marginBottom: 4, + }, + itemStyle: { + color: "var(--fg)", + padding: "2px 0", }, - labelStyle: { color: "var(--fg-muted)" }, }; export function TokenStatsDashboard({ stats }: TokenStatsDashboardProps) { From d966eea73a04eecdda550928eda683bd5f18a096 Mon Sep 17 00:00:00 2001 From: TomMass Date: Fri, 20 Mar 2026 01:35:34 +0100 Subject: [PATCH 2/2] Fix branching guide: main is the base branch, not multiuser --- AGENTS.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 53614bf..ac82795 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -162,23 +162,23 @@ Always work on **feature branches**, never commit directly to `main` or `multius ### Workflow ```bash -# 1. Create branch from the current base (usually multiuser or main) -git checkout -b feature/my-feature multiuser +# 1. Create branch from main +git checkout -b feature/my-feature main # 2. Make changes, commit git add git commit -m "Description of changes" -# 3. Push and create PR +# 3. Push and create PR targeting main git push -u origin feature/my-feature -gh pr create --base multiuser --title "Short title" --body "..." +gh pr create --base main --title "Short title" --body "..." ``` ### Rules - **One branch per logical change.** Don't mix unrelated features in one branch. -- **Base branch:** Use `multiuser` for active development. Use `main` only for release PRs. -- **PR required:** All changes go through pull requests — no direct pushes to `main` or `multiuser`. +- **Base branch:** Always `main`. All PRs target `main`. +- **PR required:** All changes go through pull requests — no direct pushes to `main`. - **Delete after merge:** Feature branches are deleted after PR is merged. ## Critical Rules