• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jhheider / penknife / 29281833392
76%

Build:
DEFAULT BRANCH: main
Ran 13 Jul 2026 08:18PM UTC
Jobs 1
Files 34
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

13 Jul 2026 08:17PM UTC coverage: 75.57% (-0.6%) from 76.182%
29281833392

push

github

web-flow
refactor: run the refresh pipeline off the render thread (#46)

The scan + per-file sync-status reads + `git status` shell-out used to run
synchronously in the main loop (App::new, the local sweep, root switch,
`r`, and the file ops). Any of them could stall the UI: a slow/stalled
mount on the scan, a hung git, or (before the dataless fix) a cloud read.

Move the live refreshes off the render thread. `start_refresh` snapshots
the inputs, then a `spawn_blocking` task scans, computes the status cache,
and runs git status; the result lands as `AsyncEvent::RefreshDone`, which
`apply_refresh` adopts. A generation counter discards a refresh superseded
by a newer one (or by a root switch). The render loop keeps drawing and
stays responsive - and quittable - even if the underlying IO stalls.

Zero-copy where it counts:
- The store lives behind `Arc<Store>` on App, so each refresh (the sweep
  fires every 5s) captures it with an O(1) `Arc::clone` instead of deep-
  copying every tracked file's mapping. Mutations go through
  `Arc::make_mut` (copy-on-write only when a write races an in-flight
  refresh) via a `store_mut()` accessor.
- The sweep fingerprints the scan (order-independent XOR of per-file
  rel_path+mtime hashes) and skips the content reads + git shell-out when
  nothing moved on disk - so an idle tree costs one metadata walk per
  interval, not thousands of reads.

Startup and the suspend/resume (`$EDITOR`, alias) paths stay synchronous;
`refresh_files` is retained for them. File-op status lines are set
synchronously (immediate feedback) and re-applied after the async refresh
so the recomputed default can't clobber them. Four file-op tests become
`#[tokio::test]` since those ops now spawn.


Claude-Session: https://claude.ai/code/session_01PykYTubrtTh4te2xg71JJS

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

83 of 224 new or added lines in 6 files covered. (37.05%)

4 existing lines in 3 files now uncovered.

7192 of 9517 relevant lines covered (75.57%)

26.45 hits per line

Uncovered Changes

Lines Coverage ∆ File
106
76.17
-10.84% crates/penknife/src/app/view.rs
22
62.14
-1.05% crates/penknife/src/app/gist.rs
5
62.61
0.59% crates/penknife/src/app/files.rs
5
75.38
0.19% crates/penknife/src/app/keys.rs
3
44.27
6.12% crates/penknife/src/app/mod.rs

Coverage Regressions

Lines Coverage ∆ File
2
44.27
6.12% crates/penknife/src/app/mod.rs
1
75.38
0.19% crates/penknife/src/app/keys.rs
1
76.17
-10.84% crates/penknife/src/app/view.rs
Jobs
ID Job ID Ran Files Coverage
1 29281833392.1 13 Jul 2026 08:18PM UTC 34
75.57
GitHub Action Run
Source Files on build 29281833392
  • Tree
  • List 34
  • Changed 7
  • Source Changed 7
  • Coverage Changed 7
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • Github Actions Build #29281833392
  • f0a83503 on github
  • Prev Build on main (#29281566581)
  • Next Build on main (#29282998591)
  • Delete
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc