Flags a stalled Cursor transcript ingest. The per-turn hooks cap how much transcript tail they read ([`crate::hooks::CURSOR_CATCH_UP_INGEST_MAX_BYTES`]), so a backlog above that cap will never drain on its own — exactly the "session recall is silently missing recent turns" failure users hit.
(dc: &mut DoctorCounters, project_path: &Path)
| 796 | /// so a backlog above that cap will never drain on its own — exactly the |
| 797 | /// "session recall is silently missing recent turns" failure users hit. |
| 798 | fn doctor_check_session_ingest(dc: &mut DoctorCounters, project_path: &Path) { |
| 799 | let db_path = crate::sessions::cursor::project_session_db_path(project_path); |
| 800 | if !db_path.exists() { |
| 801 | return; |
| 802 | } |
| 803 | // `healthcheck` is a sync trait method but runs inside the multi-thread |
| 804 | // tokio runtime, so the bounded DB read runs via block_in_place. |
| 805 | let Ok(handle) = tokio::runtime::Handle::try_current() else { |
| 806 | return; |
| 807 | }; |
| 808 | let health = tokio::task::block_in_place(|| { |
| 809 | handle.block_on(async { |
| 810 | let db = crate::sessions::cursor::open_project_session_db(project_path).await?; |
| 811 | let placeholder_paths = db.literal_workspace_placeholder_transcript_paths(10).await; |
| 812 | if !placeholder_paths.is_empty() { |
| 813 | dc.warn(&format!( |
| 814 | "Cursor transcript ingest has {} path(s) with a literal workspace placeholder; \ |
| 815 | Cursor did not expand `${{workspaceFolder}}`, so session recall will miss those transcripts", |
| 816 | placeholder_paths.len(), |
| 817 | )); |
| 818 | for path in &placeholder_paths { |
| 819 | dc.info(&format!(" - {path}")); |
| 820 | } |
| 821 | } |
| 822 | Some(db.session_ingest_health().await) |
| 823 | }) |
| 824 | }); |
| 825 | let Some(health) = health else { |
| 826 | dc.warn(&format!( |
| 827 | "could not open session store {} to check transcript ingest", |
| 828 | db_path.display() |
| 829 | )); |
| 830 | return; |
| 831 | }; |
| 832 | if health.max_transcript_pending_bytes > crate::hooks::CURSOR_CATCH_UP_INGEST_MAX_BYTES { |
| 833 | dc.warn(&format!( |
| 834 | "Cursor transcript ingest looks stalled: a transcript has {} un-ingested \ |
| 835 | byte(s) ({} byte(s) total across {} transcript(s)), exceeding the {} byte \ |
| 836 | per-transcript hook catch-up cap — it will not drain automatically and \ |
| 837 | session recall is missing those turns", |
| 838 | health.max_transcript_pending_bytes, |
| 839 | health.pending_bytes, |
| 840 | health.pending_transcripts, |
| 841 | crate::hooks::CURSOR_CATCH_UP_INGEST_MAX_BYTES, |
| 842 | )); |
| 843 | } else { |
| 844 | dc.pass(&format!( |
| 845 | "Cursor transcript ingest healthy ({} transcript(s) tracked, {} pending \ |
| 846 | byte(s), all within the per-transcript hook cap)", |
| 847 | health.tracked_transcripts, health.pending_bytes |
| 848 | )); |
| 849 | } |
| 850 | } |
| 851 | |
| 852 | fn doctor_check_plugin_rule(dc: &mut DoctorCounters, rule_path: &Path) { |
| 853 | if !rule_path.exists() { |
no test coverage detected