release: v0.11.3 — cursor scanner fix + critical follow-ups#46
Merged
Conversation
Builds on @rooty0's #45 (cursor-agent scanner fix, commits cf66a4d..8f3688f on main, authorship preserved) and adds the follow-ups that surfaced in adversarial review. Verified from #45 (correct): - depth-4 .jsonl + legacy depth-3 .txt walk - meta(key='0') store.db read with hex-encoded JSON value - orphan skip when no matching ~/.cursor/chats/<ws>/<id>/store.db - decode_dash_path with var-folders rejection - 13 well-targeted unit tests Follow-ups in this commit: Critical fixes (verified bugs in the new code): - extract_first_prompt: do not panic on inverted <user_query> tags. str::find returns the FIRST occurrence of each substring independently, so a text with </user_query> byte-preceding <user_query> gave s>e and text[s+12..e] panicked. Confirmed via rustc reproducer. Search for the closing tag AFTER the opening one. Regression test added. - extract_first_prompt: skip-not-abort on bad lines. .ok()? on both the IO read and serde_json::from_str killed extraction for the whole file on the first error, defeating the PR's own blank-summary fallback. Replaced with let Ok(...) else { continue; }, matching scanner/pi.rs. Two regression tests added (malformed JSON, invalid UTF-8). - delete_cursor_agent_session: legacy .txt transcripts are now actually removed. remove_dirs_matching_name filters on path.is_dir() so .txt files never matched; delete returned Ok(()) and the next scan resurrected the orphan. New sibling helper remove_files_matching_name handles the file form. Regression test added. - cache::CACHE_VERSION bumped 5 -> 6 so the new orphan-skip rule fires for upgraders on first launch instead of waiting for each transcript's mtime to change. Quality follow-ups: - 512 KiB byte budget on extract_first_prompt (matches the cap pi.rs got in v0.11.2 after Claude logs stalled the TUI; cursor transcripts can carry multi-MB tool-result blobs). - Stem == parent UUID invariant on the .jsonl arm. A stray agent-transcripts/<uuidA>/<uuidB>.jsonl produces a session_id that mismatches both the store.db key and cursor-agent --resume. Regression test added. - SUMMARY_MAX_CHARS constant lifted from inline 100s. - Hyphenated-path backtracker test (agent-tui-finder case). - cargo fmt over the new code. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on @rooty0's #45 (already merged to main, commits cf66a4d..8f3688f, authorship preserved as
Stan <github@rooty.name>) and adds the follow-ups that surfaced in a multi-angle adversarial review (correctness + regression + claims + test gaps + code quality).Verified from #45 (correct, kept)
.jsonl+ legacy depth-3.txtwalk — confirmed against live data (~/.cursor/projectshas 2 JSONL at depth 4, 0 TXT; previous scanner returnedNo sessions found.)SELECT value FROM meta WHERE key = '0'— confirmed viasqlite3on a realstore.db: tables areblobs+meta(key TEXT, value TEXT), value is a 506-byte hex-encoded JSON withagentId/name/createdAt/mode~/.cursor/chats/<workspace>/<session_id>/store.db(the chat session UUIDs don't overlap with the transcript UUIDs at all), andcursor-agent --resumewould refuse themvar-folders-*rejection — confirmed:~/.cursor/projectshad 17 contaminated dirs, all matchedCloses #35.
Critical follow-ups in this PR
These were confirmed bugs in
#45's new code, validated by reproducers:extract_first_promptpanic on inverted<user_query>tags —str::findreturns the FIRST occurrence of each substring independently; a text where</user_query>byte-precedes<user_query>(pasted log, AI-generated code) gaves > eandtext[s+12..e]panicked withbegin > end. Confirmed via a standalone rustc reproducer. The panic was caught by the rayon thread join, so the binary stayed up — but the entire Cursor scan silently returned 0. Fix searches for the closing tag after the opening one. (extract_first_prompt_does_not_panic_on_inverted_tags)extract_first_promptaborts on first bad line — both the per-line IO read (let line = line.ok()?;) andserde_json::from_str(line).ok()?propagatedNoneout of the whole function on the first malformed/non-UTF-8 line, silently disabling the blank-summary fallback the PR introduced. Replaced withlet Ok(...) else { continue; };, matchingscanner/pi.rs. (extract_first_prompt_skips_malformed_json_lines,extract_first_prompt_skips_invalid_utf8_lines).txtdeletion silently broken —delete_cursor_agent_sessioncalledremove_dirs_matching_name, which filters onpath.is_dir(). Legacy sessions live atagent-transcripts/<uuid>.txt(a file), so it never matched: delete returnedOk(()), the orphan persisted, and the next scan resurrected it. New sibling helperremove_files_matching_namehandles the file form. (delete_cursor_agent_removes_legacy_txt_transcript)CACHE_VERSION5 → 6 — the new orphan-skip rule fires only on fresh scans; cached0.11.xcursor entries would persist until each transcript's mtime changes, so upgraders wouldn't see the orphan fix until then. Bumped to force a one-time rescan, so the PR's "35 orphans → 0" effect actually lands for upgraders.Quality follow-ups
extract_first_prompt— matches the cap pi.rs got in v0.11.2 after Claude logs stalled the TUI. Cursor transcripts can carry multi-MB tool-result blobs, and theCACHE_VERSIONbump forces a cold rescan for everyone, so the same precaution applies..jsonlarm. A strayagent-transcripts/<uuidA>/<uuidB>.jsonlproduces asession_idthat mismatches both thestore.dbkey and whatcursor-agent --resumeexpects. Real Cursor always writes them equal, but the invariant is now explicit. (scan_from_rejects_jsonl_with_stem_mismatched_to_parent)agent-tui-finderis this very repo's name, soUsers-...-Desktop-github-agent-tui-finderwithagent/agent-tui/agent-tui-finderas siblings is the load-bearing case to lock down. (decode_dash_path_resolves_hyphenated_segments)SUMMARY_MAX_CHARS = 100lifted from inline magic numbers.cargo fmtover the new code.Test plan
cargo fmt --all --checkcargo clippy --all-targets -- -D warningscargo test— 50 passed (44 from main + 6 new regression tests)cargo build --release, installed locally,agf --version→ 0.11.3agf listworks across all agents (Claude/Codex/Hermes/etc. unchanged)agf list --agent cursor-agent→ "No sessions found." on my local data (every JSONL is an orphan, matches cursor-agent's own/resumebehavior, no crash)Closes #35.