Skip to content

refactor: edition 2024, dep currency, audit-driven modernization, CI hardening#52

Merged
subinium merged 7 commits into
mainfrom
refactor/rust-modernization
Jun 11, 2026
Merged

refactor: edition 2024, dep currency, audit-driven modernization, CI hardening#52
subinium merged 7 commits into
mainfrom
refactor/rust-modernization

Conversation

@subinium

Copy link
Copy Markdown
Owner

Six commits, each independently gated (fmt / clippy --all-targets -D warnings / test):

  1. deps — rusqlite 0.32→0.40 and toml 0.8→1 (both zero-code-change, verified empirically), lockfile refresh, rust-version = "1.88" declared (floor set by let-chains; the code already used 1.87 APIs). sha2 0.11 and superlighttui 0.21 evaluated and deferred (breaking; notes in commit message).
  2. edition 2024cargo fix --edition (test-only unsafe env wraps in pi.rs) + cargo clippy --fix collapsing ~37 if-let staircases into let-chains.
  3. ci — clippy --all-targets (tests were unlinted in CI), rust-cache, per-ref concurrency cancel, Windows clippy, --locked everywhere; release.yml gains a tag↔Cargo.toml version guard, loud checksum failures, and a graceful publish skip when CARGO_REGISTRY_TOKEN is unset (every release was ending red).
  4. scanner — shared collapse_whitespace / project_name_from_path / push_concat_titles helpers replacing 5–6 duplicated sites each; gemini UTF-8 char-boundary panic fix (+CJK regression test); cursor hex_decode non-ASCII guard; claude history orphan pre-filter (same optimization codex got in v0.11.4); read_first_line 512 KiB budget; let-else flattening (25 sites).
  5. tui — per-frame allocation cuts (render_chunks by-value, update_filter/apply_sort clone removal, detect_editor process-lifetime cache); deletes now only remove rows from the UI when the filesystem delete succeeded; merge_agent_sessions no-op deleted; watch clamps selection when a refresh shrinks the list.
  6. coreAgent derives serde (hand-rolled string maps deleted; on-disk cache format pinned byte-identical by a new round-trip test); agf resume --list 0 underflow panic fixed; delete.rs let-else ladders; PR fix(shell): default to PowerShell on native Windows without the wrapper #49 follow-up shell tests (uppercase .EXE, empty SHELL) and the MSYSTEM empty-set nit.

Provenance: findings come from a multi-agent audit (3 area auditors + per-finding adversarial verification; 32 confirmed, 2 rejected) plus an empirical deps/edition trial. Real-data invariant: agf list --format json returns the identical 585 sessions before and after.

🤖 Generated with Claude Code

subinium and others added 7 commits June 11, 2026 10:36
Both major bumps compile with zero code changes (rusqlite's bundled
SQLite moves to libsqlite3-sys 0.38; agf's toml surface — from_str,
Table, Value variants — is stable across 0.8→1). Lockfile refreshed.

rust-version starts at 1.88: the code already uses
usize::is_multiple_of (1.87, caught by clippy::incompatible_msrv once
an MSRV is declared) and the edition-2024 migration that follows
adopts let-chains (1.88). Highest dependency MSRV is 1.85.

sha2 stays 0.10: 0.11 breaks the gemini hash formatting (digest 0.11
output no longer implements LowerHex). superlighttui stays 0.20: 0.21
adds #[must_use] to separator_colored, wanting 29 call-site updates —
deferred to its own change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mechanical migration, zero behavior change:
- cargo fix --edition: wraps the test-only HOME env mutations in
  src/scanner/pi.rs in unsafe {} (env mutation is unsafe in 2024)
- cargo clippy --fix: collapses ~37 nested if-let staircases into
  let-chains across the scanners and TUI (collapsible_if), now that
  edition 2024 stabilizes them
- cargo fmt over the result

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ci.yml:
- clippy runs --all-targets so tests are linted like the local gate;
  it subsumes cargo check, so the separate check job is dropped
- Swatinem/rust-cache@v2 on compiling jobs (bundled SQLite C build was
  recompiled from scratch every run)
- workflow concurrency cancels superseded runs on the same ref
- windows job now lints (#[cfg(windows)] paths were never clippy'd)
- --locked everywhere cargo resolves, so CI tests the committed lockfile

release.yml:
- verify job fails fast when the tag does not match Cargo.toml version
- checksums step fails loudly when an artifact is missing instead of
  silently skipping it
- publish job skips gracefully when CARGO_REGISTRY_TOKEN is unset
  instead of painting every release red (the secret is not configured)
- --locked on release builds and publish

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- fix(gemini): char-boundary-safe 1 KB window in extract_summary_partial — a CJK char straddling byte 1024 panicked and silently erased every Gemini session; + regression test
- fix(cursor): hex_decode rejects non-ASCII input before byte-slicing so a corrupt store.db yields None instead of killing the scanner thread; + regression test
- perf(claude): pre-filter history.jsonl entries against existing_ids so orphaned sessions never accumulate SessionData/summary tuples (mirrors codex v0.11.4 pre-filter)
- perf(claude/codex): drop per-line String alloc in history.jsonl hot loops (borrowed trim instead of to_owned)
- perf(mod): cap read_first_line at 512 KiB via Read::take so a pathological newline-free .jsonl is no longer slurped whole; + tests
- new shared helpers in scanner/mod.rs, each unit-tested: collapse_whitespace (claude/gemini x3/hermes), project_name_from_path (codex x2/kiro/opencode/pi/cursor), push_concat_titles (opencode/hermes, also drops alloc-per-duplicate)
- idiom(codex): 15 match-Some/None/Ok/Err early-exit sites flattened to let-else; find_state_db collect+sort+next replaced with Iterator::max
- idiom(cursor): 10 match-continue sites flattened to let-else; encoded_dir now stays a &str (one less alloc per session file)
- hermes::message_preview reduced to collapse_whitespace + existing char_prefix
- pi tests: replaced edition-migration TODO comments on unsafe env access with HOME_LOCK serialization notes
- edition-2024 let-chain flattening verified already applied by migration commit 934bd3b (kiro/gemini pyramids correctly excluded — intermediate bindings make them non-chainable)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- render_chunks consumes its Vec, dropping a String clone per chunk per visible row per frame
- update_filter moves match positions out of fuzzy results instead of cloning each Vec per keystroke
- apply_sort borrows pinned_sessions and uses stable sort_by_key(!contains) instead of clone + 0/1 rank closure
- detect_editor caches the config/env editor lookup in a process-wide OnceLock (was disk read + TOML parse per frame while the action menu is open)
- delete confirm (single + bulk) only removes rows and decrements agent counts when delete_session returns Ok, so failed deletes stay visible
- watch: clamp selected when a background refresh shrinks the session list (stale cursor blanked the viewport)
- merge_agent_sessions: delete dead selected_id snapshot + discarded position scan (apply_sort already restores the cursor)
- new_session_with_flags returns String (the Option was never None); both dispatchers unwrapped
- dispatch_agent_option / dispatch_mode_option flattened to single let-chains
- App::new: drop redundant installed_agents() clone
- cycle_summary: let-else + entry API (one hash lookup instead of two), wrap-around semantics preserved
- map(..).unwrap_or(..) chains -> is_some_and / is_ok_and / is_none_or / map_or at the audited tui/watch sites

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l tests

- model.rs: derive serde::Serialize/Deserialize on Agent (unit variants keep the exact on-disk strings)
- cache.rs: re-type CachedSession.agent as Agent and CacheFile.agents as HashMap<Agent, AgentCache>; delete agent_from_str/agent_to_str; from_cached now returns Session (no Option)
- cache.rs: add round-trip test pinning the on-disk format (CacheFile round-trips, Agent serializes as "ClaudeCode")
- cache.rs: delete clone_cached — write_cache carry-over moves entries out of the owned prior CacheFile via HashMap::remove
- cache.rs: cache the AGF_DEBUG probe in a OnceLock-backed debug_enabled() helper (both cache.rs call sites)
- main.rs: guard `agf resume --list 0 <query>` usize underflow/panic with take(n.max(1))
- main.rs: replace manual --version argv Vec collect with std::env::args().any(...)
- delete.rs: convert four match-Ok/Err-continue ladders to let-else; .filter_map(|e| e.ok()) -> .flatten() at all 8 sites; map().unwrap_or(false) -> is_some_and at 2 sites
- shell.rs: MSYSTEM empty-but-set no longer routes native Windows to Posix (is_some_and)
- shell.rs: add default_shell_handles_uppercase_exe_and_empty_shell test (lowercase-before-trim order, empty SHELL = unset)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The only user of the Write trait is the unix-gated place_session test
fixture; the ungated import was an unused-import error on the new
Windows clippy CI job (which is exactly the platform gap that job
exists to catch).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@subinium subinium merged commit 54062cd into main Jun 11, 2026
4 checks passed
@subinium subinium mentioned this pull request Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant