who.rs: Add INDEXED BY idx_notes_diffnote_path_created to all DiffNote query paths (expert, expert_details, reviews, path probes, suffix_probe). SQLite planner was choosing idx_notes_system (106K rows, 38%) over the partial index (26K rows, 9.3%) when LIKE predicates are present. Measured: expert 1561ms->59ms (26x), reviews ~1200ms->16ms (75x). stats.rs: Replace 12+ sequential COUNT(*) queries with conditional aggregates (SUM(CASE WHEN...)) and use FTS5 shadow table (documents_fts_docsize) instead of virtual table for counting. Measured: warm 109ms->65ms (1.68x).
177 lines
9.6 KiB
Markdown
177 lines
9.6 KiB
Markdown
I reviewed the full PRD and avoided everything listed under `## Rejected Recommendations`.
|
||
These are the highest-impact revisions I’d make.
|
||
|
||
1. Stable list pagination via snapshot fences
|
||
Why this improves the plan: your keyset cursor is deterministic for sort/filter, but still vulnerable to duplicates/skips if sync writes land between page fetches. Add a per-browse snapshot fence so one browse session sees a stable dataset.
|
||
Tradeoff: newest rows are hidden until refresh, which is correct for deterministic triage.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 5.2 Issue List
|
||
- **Pagination:** Windowed keyset pagination with explicit cursor state.
|
||
+ **Pagination:** Windowed keyset pagination with explicit cursor state.
|
||
+ **Snapshot fence:** On list entry, capture `snapshot_upper_updated_at` (ms) and pin all
|
||
+ list-page queries to `updated_at <= snapshot_upper_updated_at`. This guarantees no duplicate
|
||
+ or skipped rows during scrolling even if sync writes occur concurrently.
|
||
+ A "new data available" badge appears when a newer sync completes; `r` refreshes the fence.
|
||
|
||
@@ 5.4 MR List
|
||
- **Pagination:** Same windowed keyset pagination strategy as Issue List.
|
||
+ **Pagination:** Same strategy plus snapshot fence (`updated_at <= snapshot_upper_updated_at`)
|
||
+ for deterministic cross-page traversal under concurrent sync writes.
|
||
|
||
@@ 4.7 Navigation Stack Implementation
|
||
+ Browsing sessions carry a per-screen `BrowseSnapshot` token to preserve stable ordering
|
||
+ until explicit refresh or screen re-entry.
|
||
```
|
||
|
||
2. Query budgets and soft deadlines
|
||
Why this improves the plan: currently “slow query” is handled mostly by cancellation and stale-drop. Add explicit latency budgets so UI responsiveness stays predictable under worst-case filters.
|
||
Tradeoff: sometimes user gets partial/truncated results first, followed by full results on retry/refine.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 4.5 Async Action System
|
||
+ #### 4.5.2 Query Budgets and Soft Deadlines
|
||
+ Each query type gets a budget:
|
||
+ - list window fetch: 120ms target, 250ms hard deadline
|
||
+ - detail phase-1 metadata: 75ms target, 150ms hard deadline
|
||
+ - search lexical/hybrid: 250ms hard deadline
|
||
+ On hard deadline breach, return `QueryDegraded { truncated: true }` and show inline badge:
|
||
+ "results truncated; refine filter or press r to retry full".
|
||
+ Implementation uses SQLite progress handler + per-task interrupt deadline.
|
||
|
||
@@ 9.3 Phase 0 — Toolchain Gate
|
||
+ 26. Query deadline behavior validated: hard deadline cancels query and renders degraded badge
|
||
+ without blocking input loop.
|
||
```
|
||
|
||
3. Targeted cache invalidation and prewarm after sync
|
||
Why this improves the plan: `invalidate_all()` after sync throws away hot detail cache and hurts the exact post-sync workflow you optimized for. Invalidate only changed keys and prewarm likely-next entities.
|
||
Tradeoff: slightly more bookkeeping in sync result handling.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 4.1 Module Structure
|
||
- entity_cache.rs # Bounded LRU cache ... Invalidated on sync completion.
|
||
+ entity_cache.rs # Bounded LRU cache with selective invalidation by changed EntityKey
|
||
+ # and optional post-sync prewarm of top changed entities.
|
||
|
||
@@ 4.4 App — Implementing the Model Trait (Msg::SyncCompleted)
|
||
- // Invalidate entity cache — synced data may have changed.
|
||
- self.entity_cache.invalidate_all();
|
||
+ // Selective invalidation: evict only changed entities from sync delta.
|
||
+ self.entity_cache.invalidate_keys(&result.changed_entity_keys);
|
||
+ // Prewarm top N changed/new entities for immediate post-sync triage.
|
||
+ self.enqueue_cache_prewarm(&result.changed_entity_keys);
|
||
```
|
||
|
||
4. Exact “what changed” navigation without new DB tables
|
||
Why this improves the plan: your summary currently uses timestamp filter; this can include unrelated updates and miss edge cases. Keep an in-memory delta ledger per sync run and navigate by exact IDs.
|
||
Tradeoff: small memory overhead per run; no schema migration required.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 5.9 Sync (Summary mode)
|
||
-- `i` navigates to Issue List pre-filtered to "since last sync" (using `sync_status.last_completed_at` timestamp comparison)
|
||
-- `m` navigates to MR List pre-filtered to "since last sync" (using `sync_status.last_completed_at` timestamp comparison)
|
||
+- `i` navigates to Issue List filtered by exact issue IDs changed in this sync run
|
||
+- `m` navigates to MR List filtered by exact MR IDs changed in this sync run
|
||
+ (fallback to timestamp filter only if run delta not available)
|
||
|
||
@@ 10.1 New Files
|
||
+crates/lore-tui/src/sync_delta_ledger.rs # In-memory per-run exact changed/new IDs (issues/MRs/discussions)
|
||
```
|
||
|
||
5. Adaptive render governor (runtime performance safety)
|
||
Why this improves the plan: capability detection is static; you also need dynamic adaptation when frame time/backpressure worsens (SSH, tmux nesting, huge logs).
|
||
Tradeoff: visual richness may step down automatically under load.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 3.4.1 Capability-Adaptive Rendering
|
||
+#### 3.4.2 Adaptive Render Governor
|
||
+Runtime monitors frame time and stream pressure:
|
||
+- if frame p95 > 40ms or sync drops spike, switch to lighter profile:
|
||
+ plain markdown, reduced tree guides, slower spinner tick, less frequent repaint.
|
||
+- when stable for N seconds, restore previous profile.
|
||
+CLI override:
|
||
+`lore tui --render-profile=auto|quality|balanced|speed`
|
||
|
||
@@ 9.3 Phase 0 — Toolchain Gate
|
||
+27. Frame-time governor validated: under induced load, UI remains responsive and input latency
|
||
+stays within p95 < 75ms while auto-downgrading render profile.
|
||
```
|
||
|
||
6. First-run/data-not-ready screen (not an init wizard)
|
||
Why this improves the plan: empty DB or missing indexes will otherwise feel broken. A dedicated read-only readiness screen improves first impression and self-recovery.
|
||
Tradeoff: one extra lightweight screen/state.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 4.3 Core Types (Screen enum)
|
||
Sync,
|
||
Stats,
|
||
Doctor,
|
||
+ Bootstrap,
|
||
|
||
@@ 5.11 Doctor / Stats (Info Screens)
|
||
+### 5.12 Bootstrap (Data Readiness)
|
||
+Shown when no synced projects/documents are present or required indexes are missing.
|
||
+Displays concise readiness checks and exact CLI commands to recover:
|
||
+`lore sync`, `lore migrate`, `lore --robot doctor`.
|
||
+Read-only; no auto-execution.
|
||
```
|
||
|
||
7. Global project scope pinning across screens
|
||
Why this improves the plan: users repeatedly apply the same project filter across dashboard/list/search/timeline/who. Add a global scope pin to reduce repetitive filtering and speed triage.
|
||
Tradeoff: must show clear “scope active” indicator to avoid confusion.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 4.1 Module Structure
|
||
+ scope.rs # Global project scope context (all-projects or pinned project set)
|
||
|
||
@@ 8.1 Global (Available Everywhere)
|
||
+| `P` | Open project scope picker / toggle global scope pin |
|
||
|
||
@@ 4.10 State Module — Complete
|
||
+pub global_scope: ScopeContext, // Applies to dashboard/list/search/timeline/who queries
|
||
|
||
@@ 10.11 Action Module — Query Bridge
|
||
- pub fn fetch_issues(conn: &Connection, filter: &IssueFilter) -> Result<Vec<IssueListRow>, LoreError>
|
||
+ pub fn fetch_issues(conn: &Connection, scope: &ScopeContext, filter: &IssueFilter) -> Result<Vec<IssueListRow>, LoreError>
|
||
```
|
||
|
||
8. Concurrency correctness tests for pagination and cancellation races
|
||
Why this improves the plan: current reliability tests are good, but missing a direct test for duplicate/skip behavior under concurrent sync writes while paginating.
|
||
Tradeoff: additional integration test complexity.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 9.2 Phases (Phase 5.5 — Reliability Test Pack)
|
||
+ Concurrent pagination/write race tests :p55j, after p55h, 1d
|
||
+ Query deadline cancellation race tests :p55k, after p55j, 0.5d
|
||
|
||
@@ 9.3 Phase 0 — Toolchain Gate
|
||
+28. Concurrent pagination/write test proves no duplicates/skips within a pinned browse snapshot.
|
||
+29. Cancellation race test proves no cross-task interrupt bleed and no stuck loading state.
|
||
```
|
||
|
||
9. URL opening policy v2: allowlisted GitLab entity paths
|
||
Why this improves the plan: host validation is necessary but not always sufficient. Restrict default browser opens to known GitLab entity paths and require confirmation for unusual paths on same host.
|
||
Tradeoff: occasional extra prompt for uncommon but valid URLs.
|
||
|
||
```diff
|
||
diff --git a/docs/plans/gitlore-tui-prd-v2.md b/docs/plans/gitlore-tui-prd-v2.md
|
||
@@ 3.1 Risk Matrix
|
||
-| Malicious URL in entity data opened in browser | Medium | Low | URL host validated against configured GitLab instance before `open`/`xdg-open` |
|
||
+| Malicious URL in entity data opened in browser | Medium | Low | Validate scheme+host+port and path pattern allowlist (`/-/issues/`, `/-/merge_requests/`, project issue/MR routes). Unknown same-host paths require explicit confirm modal. |
|
||
|
||
@@ 10.4.1 Terminal Safety — Untrusted Text Sanitization
|
||
- pub fn is_safe_url(url: &str, allowed_origins: &[AllowedOrigin]) -> bool
|
||
+ pub fn classify_safe_url(url: &str, policy: &UrlPolicy) -> UrlSafety
|
||
+ // UrlSafety::{AllowedEntityPath, AllowedButUnrecognizedPath, Blocked}
|
||
```
|
||
|
||
These 9 changes are additive, avoid previously rejected ideas, and materially improve determinism, responsiveness, post-sync usefulness, and safety without forcing a big architecture reset. |