# V2.6 Hot Query Budget And Index Audit This document records the V2.6 query budget for the large-data harness and the index contracts that keep hot APIs on partition keys. ## Commands Offline contract audit: ```bash node scripts/explain-hot-queries.mjs --dry-run ``` Database explain audit: ```bash DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm pnpm perf:explain ``` Strict plan mode is available for seeded medium/large databases: ```bash DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm pnpm perf:explain -- --strict-plan ``` `--strict-plan` fails on sequential scans. It is useful after the medium fixture is seeded and analyzed, but not required for empty or tiny local databases where PostgreSQL may choose a sequential scan correctly. ## Budgets | Area | Query Shape | Budget | | --- | --- | --- | | `health/version` | no database query | HTTP p95 <= 500ms | | Requirement pool | `requirements.product_id` + optional filters/search, cursor, `created_at` sort | HTTP p95 <= 1000ms | | Version detail | root version plus child rows by `version_id` | HTTP p95 <= 1500ms | | Workspace | owner/assignee unfinished rows | HTTP p95 <= 1200ms | | Xiaobao warnings | non-`on_track` summaries by score | HTTP p95 <= 1000ms | | Xiaobao dirty queue | `dirty=true` summaries ordered by `updated_at` | background batch <= 100 rows | | Audit search adapter | V2.5 audit table pending; `ai_logs` is the current AI audit surface | explain-only contract | ## Required Index Contracts V2.6 keeps the existing partition prefixes: - Requirement pool queries must include `productId`; `requirements` is hash-partitioned by `product_id`. - Version detail child queries must include `versionId`; `dev_tasks`, `test_cases`, and `bugs` are hash-partitioned by `version_id`. - Append evidence tables stay range-partitioned by `created_at`; background workers must still filter by `version_id`, `user_id`, or date before scanning. Added in migration `20260708030000_v26_hot_query_indexes`: | Index | Purpose | | --- | --- | | `projects_product_created_at_idx` | product-scoped project list | | `versions_product_created_at_idx` | product-scoped version list | | `versions_product_project_created_at_idx` | project-scoped version list | | `version_plans_owner_open_due_idx` | workspace plan queue | | `dev_tasks_assignee_open_priority_idx` | workspace dev task queue | | `test_cases_assignee_open_priority_idx` | workspace test case queue | | `bugs_version_status_priority_updated_at_idx` | version detail bug ordering | | `bugs_assignee_open_priority_idx` | workspace bug queue | | `test_cases_version_round_status_updated_at_desc_idx` | version detail test case ordering | | `xiaobao_risk_summaries_warning_score_idx` | manager Xiaobao warning list | | `xiaobao_risk_summaries_dirty_updated_at_idx` | background Xiaobao dirty summary queue | | `work_activities_version_occurred_at_idx` | Xiaobao evidence recompute | Existing V2.2 indexes remain part of the contract, including requirement pool indexes, version child indexes, workspace partial indexes, task worklog date indexes, Xiaobao snapshot/insight indexes, and `ai_logs` operation/status indexes. ## Audit Adapter Note The V2.5 RBAC/audit contract is not present in this branch. V2.6 therefore documents `audit.searchAdapter` as an adapter target instead of inventing a temporary audit table. When audit lands, the expected query shape should be: ```sql SELECT * FROM audit_events WHERE product_id = $1 AND created_at >= $2 ORDER BY created_at DESC LIMIT 100; ``` Expected future index: `(product_id, created_at DESC)` plus actor/resource indexes required by the audit module. Until then, `ai_logs_operation_created_at_idx` and `ai_logs_status_created_at_idx` cover AI operation audit searches only.