Skip to content

Windows speed, memory and a right-click menu - #1

Merged
mgpai22 merged 93 commits into
mainfrom
context-menu
Oct 3, 2026
Merged

mgpai22 merged 93 commits into
mainfrom
context-menu

Conversation

@mgpai22

@mgpai22 mgpai22 commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Brings the fork's main up to date with upstream main (158f9cc) plus:

  • Windows speed and memory work from perf: start scans from the last one, and keep the tree in half the memory tobi/disktree#48: launches start from the kept tree and the NTFS change journal; the tree is flat and about half the memory.
  • A right-click menu drawn in disktree's own style: Open, Show in File Explorer, Copy path, Mark, Open terminal here. On Windows it also lists Explorer's own items (Open with, Send to, 7-Zip, Properties, ...) without Delete and Cut.
  • After an Explorer item or the terminal changes a folder, disktree re-walks only that folder and watches it for 10 minutes, refreshing 2 s after changes stop.

Checks: cargo xtask lint and cargo xtask test pass on Windows; the menu, terminal and folder refresh were exercised in the running app.

ashwch and others added 30 commits September 25, 2026 15:40
cmux terminates the foreground process group when AppKit activates an executable reached through the macOS command-line symlink.

Restart interactive cmux launches once in their own process group before AppKit starts. Preserve arguments, prevent recursive restarts with an environment marker, and leave non-cmux and non-interactive launches unchanged.
`cargo xtask bundle` from a fresh clone failed with `rustc 1.94.0 is not
supported by the following package: xtask@0.10.1 requires rustc 1.97` when
the machine's `rustup default` was older than the workspace MSRV. The message
blames xtask only because `.cargo/config.toml` aliases `cargo xtask` to
`cargo run -p xtask`, so it is the first package in the build graph; cargo
rejects the whole workspace before anything compiles.

CI pins 1.97 but nothing provisioned it for builders. rust-toolchain.toml
pins the same version, with the clippy and rustfmt that `make lint` needs, so
rustup fetches it on the first build and local lints match the gate.

Verified from this commit: `cargo xtask --help` (the command that failed)
runs, `cargo xtask lint` is green, and `cargo xtask bundle` produces
target/bundle/disktree-0.10.1-aarch64-macos.zip.
The README has always said the mouse's side buttons retrace the visited
directories, and on_mouse_down had the two MouseButton::Navigate arms for
it, but nothing ever delivered buttons 8 and 9: the mosaic registered
listeners per button, for Left and Middle only. The arms were dead code.

Replaced the two registrations with on_any_mouse_down, so every button
reaches on_mouse_down and the Navigate arms work. One listener rather
than four, and the per-button form would have needed a new registration
for each button the platform names.

Guarded the arms on Screen::Explore, matching the alt-arrow path: on the
review screen the marked list is not somewhere history should move.

The help screen and the hover card on `<` and `>` named only the keys,
so the in-app bindings were narrower than the README's. Both now mention
the side buttons.
Elevated scans of a whole NTFS drive read the master file table in large parallel reads instead of walking directories; any failure falls back to the walk. The walk also allocates once per entry, tallies progress per directory and folds hardlink de-dup into the parallel aggregate pass.
# Conflicts:
#	crates/disktree-app/src/tests.rs
# Conflicts:
#	crates/disktree-app/src/tests.rs
# Conflicts:
#	crates/disktree-app/src/tests.rs
# Conflicts:
#	README.md
#	crates/disktree-app/src/views.rs
#	crates/disktree-core/src/windows.rs
Integrate Windows scanning, drive picker and navigation fixes
A WSL disk image grew 1.1 GB with no journal entry. Files last seen open
and the 1,024 largest are measured again on every reuse.
current_file and sizes_by_id both asked for FILE_STANDARD_INFO.
sizes_by_id read it into the struct, whose flags are Rust bools a
kernel byte need not be valid for; both now read it as words.
Every read of the body reopens the handle whose header was checked,
not the path, so all reads see the same file.
A cache is opened relative to its directory with NtCreateFile, and a
link in its place is not followed.
The partial is made and renamed relative to the open directory, so a
link planted above it cannot redirect either; a failed save deletes it.
An elevated process keeps its caches in admin, made with an owner and
ACL only Administrators and SYSTEM hold, and never reads the user's.
An elevated scan reads a cache only when its directory and the very
handle it reads are owned by Administrators or SYSTEM under a protected
ACL that grants no one else write access. Anything else is a whole scan.
README describes the admin cache directory and its checks; AGENTS
lists the rule as an invariant and names where it is enforced.
The tree is no longer a boxed node per entry (120 B and a heap block for
its name) but flat lists: a 32 B entry per file or folder, a 56 B record
per directory, names in text segments. The walk places each directory's
ordered run into its thread's fixed segment as the directory finishes,
charging a hardlinked file at its first listed name, so no whole-tree
aggregate pass remains. The file table reader builds the same tree in
parallel and hands it over as is: no node tree beside the flat one, and
the saving thread writes the tree the app holds instead of building a
second. The walk cache is read and written as a stream straight into and
out of that tree. The app reads it through a Copy `Node` view; a metric
switch reorders runs in place.

Measured on this machine (i7-12800HX), base e594324 vs this, interleaved,
first pair dropped, medians:
- home walk C:\Users\shish (3.67 M files, 551 k dirs), 6 pairs:
  cold peak commit 1061 -> 402 MB, held private 757 -> 304 MB, wall
  9256 -> 9604 ms (+3.8%), CPU 83.7 -> 81.5 s; warm peak 850 -> 261 MB,
  held 693 -> 253 MB, wall 1025 -> 580 ms, CPU 1055 -> 594 ms.
- elevated C:\ tree from the real 4.87 M-entry table (lab, 6 pairs):
  cold peak 1820 -> 1072 MiB, held 845 -> 337 MiB, hand-over 452 ->
  360 ms; warm peak 1073 -> 284 MiB, held 784 -> 284 MiB, load to tree
  290 -> 76 ms, CPU 1400 -> 483 ms.
Store only the 256-record pages touched by planned reads. Keep original record numbers for links and journal refreshes. Cover gaps, shared pages, and refreshes into absent pages.

Six alternating real-table pairs, first discarded: peak 1837 to 1616 MiB; node plus flat build 1094 to 1046 ms and CPU 2796 to 2641 ms. Tree-ready phase 444 to 476 ms; live elevated whole-scan confirmation remains required.
Keep direct live/directory flags and full sizes, times, sequences and name counts. Store only hidden, evicted and reparse facts consumed by traversal. Preserve ordinary-tag precedence over name hints and extensions.

Seven interleaved real-table trios, first discarded: versus sparse40, peak 1698875392 to 1646981120 bytes, build wall 1178.5 to 1135.5 ms, CPU 2789.5 to 2820.5 ms. Tree and tag-precedence checks pass; elevated confirmation remains required.
Adapt the tag-precedence assertion to the resident Tree API after rebase onto 5673a24. Seven matched real-table pairs, first discarded: peak 1101.0 to 834.3 MiB; tree-ready wall 378.5 to 393.5 ms and CPU 1547 to 1476 ms. Full decode/build wall 746 to 724 ms, CPU 2297 to 2046 ms.
An entry keeps its name's length in 16 bits, so a checksummed walk
cache holding a longer name below the root loaded it as an empty name.
The reader now refuses such a record before taking its bytes, and the
next scan walks again. No file system names a file that long; this
only closes the loader to a forged or damaged file.
A whole read stops at 1024 levels, but a kept tree came back without
that bound, and a journal that moves a deep folder into another could
take the patched tree past it: a 50,000-level kept tree overflowed the
stack in the filter. The patch, which every resumed scan goes through,
now checks the finished tree's depth from the root and asks for a
whole read past it. Also drops a trailing blank line rustfmt refused.
A Windows file id keeps the record's reuse count above bit 48, so every
id missed the bitmap and went into the sharded hash set: about 60 MiB
for a home folder's 3.7 M files, held to the end of a cold walk. A walk
that stays on one NTFS volume and grafts nothing kept from an earlier
scan meets only current ids, where one record is one file, so it keys
the bitmap by record. A widening walk, another file system, a share or
a walk that follows links keeps whole keys. Ported from Fable's
f398c1d, with the NTFS check added.

Cold walk of C:\Users\shish, launch probe, 6 interleaved pairs, first
dropped, medians: peak commit 406 -> 349 MiB; private held after the
scan 358 -> 292 MiB; wall 83 -> 81 s, process CPU 164 -> 165 s (both
noisy); warm unchanged at 252 MiB.
A run's name offsets, a segment's entry counts and the directories a
builder hands out are 32-bit, and were narrowed with `as`: past 4 GiB of
names in one run, or 4 G directories, they wrapped into wrong names or
reused numbers. Builder::place now refuses such a run, and a refused
reserve takes nothing. The file table build then stops and the walk
measures instead, as the old build did; a walk shows such a directory
unreadable, its entries unknown; a kept tree too large for one segment
is not saved. No real volume comes near these limits.
A relisted directory's removed subfolders kept their parent link and
their entries, although a dropped directory has neither (tree.rs). They
now get both cleared, beneath them too, while their files are marked
for the alias refresh as before. Also wraps two comment lines to 80
columns.
With hardlinks counted once, a file weighs under one name and nothing
under the others. When the folder holding the charged name went out of
view (hidden, or made a cloud placeholder) while the file itself did
not change, a resumed scan dropped that name and left the others at
nothing, so the totals above them came up short until the next whole
read. The patch now asks for a whole read in that case. Not a
regression: the old patch did the same.
The depth check a resume makes walked every entry a level at a time,
and an elevated warm launch took about 20 ms longer for it (5 pairs,
warm2 194 -> 213 ms). It now climbs each directory's parent links once,
keeping the levels found, which is a pass over the directories only.
It also refuses a live directory hanging from nothing, or parents that
loop, which the old walk passed over.
A cold NTFS folder walk encoded and wrote its ~150 MB snapshot before handing the tree over, so the treemap waited out the save. The walk now keeps the tree on a thread of its own that shares the Arc, the way the file-table reader already did; both share one save slot in scan.rs, which a scan waits on before it reads a kept tree and which scan::wait_for_cache joins. A cancelled walk still keeps nothing.
One right-click menu, drawn by disktree on every platform. On Windows it
lists Explorer's rows below disktree's own: the shell builds them into a
Win32 menu that is never shown, and shell_menu.rs reads it into rows with
their labels, state, icons and submenus, without Delete and Cut. Submenus
open as flyouts by pointer or arrow keys; a pick runs the shell command by
its id. The rows load after the menu first draws and are read again over
the next seconds, since Send to and Defender add theirs late.

The native popup, its window-procedure swap and the release-to-open
listener are gone.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. PR-changed functions: 0/170 verified (0 proven, 0 may-equivalent, 0 distinguished) · 170 not verified (25 unsupported, 145 over cap).

Not verified on this run: parse\_args (unsupported), run (unsupported), file (unsupported), find (unsupported), find\_matches\_whole\_components\_only (unsupported), refresh (unsupported), tree (unsupported), apply\_filter (unsupported), begin\_removal (unsupported), breadcrumbs (unsupported), and 15 more; 145 changed function(s) beyond the cap and 0 skipped when the time budget ran out.


Graphify review — findings

Adds an elevated Windows path that reads a whole NTFS drive straight from the master file table instead of walking it, and resumes later scans from the last snapshot plus the change journal. It reads only caches kept under an Administrators-only ACL and falls back to a full scan otherwise. The tree becomes flat, with arena names and per-run totals and ordering, and a hardlinked file is charged under the first name listed. Removal now refuses each profile directory under FOLDERID_UserProfiles, and the app gains a volume picker (v), esc to stop a scan, single-folder refresh with a folder watch, and Explorer's rows in the right-click menu.

Worth a look

  • Windows-only icon check has no OS guard — .github/workflows/ci.yml:102 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review

Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1310 functions depend on the 1310 functions this change touches.

Health — this change adds coupling hotspots:

  • new: refuse() — 10 callers, 10 callees
  • new: remove_permanently() — 10 callers, 5 callees
  • new: scan_blocking() — 3 callers, 16 callees
  • new: guard_key() — 12 callers, 3 callees
  • new: classify_where() — 4 callers, 8 callees
  • new: selection_section() — 1 callers, 21 callees
  • new: encode() — 2 callers, 8 callees
  • new: node_card() — 2 callers, 7 callees
  • …and 50 more — each is listed as a finding

Verification — 1310 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1310 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify parse\_args.

The verifier did not have enough to check parse\_args, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: signature changed (() -> Result<Args>) → ((mut args: implIterator<Item=std::ffi::OsString>) -> Result<Args>) — a single call cannot feed both versions

Could not verify: Could not verify run.

The verifier did not have enough to check run, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported return type: Result<()>

Could not verify: Could not verify file.

The verifier did not have enough to check file, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: signature changed ((name: &str, bytes: u64) -> Node) → ((name: &str, bytes: u64) -> Draft) — a single call cannot feed both versions

Could not verify: Could not verify find.

The verifier did not have enough to check find, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: signature changed ((root_path: &Path, root: &Node, path: &Path) -> Option<&Node>) → ((root_path: &Path, root: Node<'a>, path: &Path) -> Option<Node<'a>>) — a single call cannot feed both versions

Could not verify: Could not verify find\_matches\_whole\_components\_only.

The verifier did not have enough to check find\_matches\_whole\_components\_only, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unit-returning functions have no value to compare

Could not verify: Could not verify refresh.

The verifier did not have enough to check refresh, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify tree.

The verifier did not have enough to check tree, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: signature changed (() -> Node) → (() -> Tree) — a single call cannot feed both versions

Could not verify: Could not verify apply\_filter.

The verifier did not have enough to check apply\_filter, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify begin\_removal.

The verifier did not have enough to check begin\_removal, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify breadcrumbs.

The verifier did not have enough to check breadcrumbs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify can\_start\_over.

The verifier did not have enough to check can\_start\_over, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify crumbs\_for\_path.

The verifier did not have enough to check crumbs\_for\_path, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify current.

The verifier did not have enough to check current, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify dispatch\_key.

The verifier did not have enough to check dispatch\_key, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify enter.

The verifier did not have enough to check enter, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify node\_at.

The verifier did not have enough to check node\_at, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify on\_explore\_key.

The verifier did not have enough to check on\_explore\_key, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify on\_mouse\_down.

The verifier did not have enough to check on\_mouse\_down, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify poll\_scan\_once.

The verifier did not have enough to check poll\_scan\_once, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify prepare.

The verifier did not have enough to check prepare, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify ranked\_siblings.

The verifier did not have enough to check ranked\_siblings, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify refresh\_insights.

The verifier did not have enough to check refresh\_insights, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify render.

The verifier did not have enough to check render, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify reveal\_target.

The verifier did not have enough to check reveal\_target, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

Could not verify: Could not verify set\_root.

The verifier did not have enough to check set\_root, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: unsupported: method receiver/self type not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver

· 20 grounded finding(s) anchored inline below; 27 more finding(s) on lines outside this diff (see the check run); 11 additional anchorable finding(s) not shown (cap).

@@ -73,7 +83,31 @@ fn main() -> Result<()> {
}

fn run() -> Result<()> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — run()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.


/// Enter must reach the picker even when its dialog owns keyboard focus.
#[gpui_kit::test]
fn enter_in_the_focused_volume_picker_scans_the_selected_root(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — enter_in_the_focused_volume_picker_scans_the_selected_root()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

/// Escape stops a first scan. What the walk found so far is not shown as a
/// tree, a late result is ignored, and `r` starts over.
#[gpui_kit::test]
fn escape_cancels_the_first_scan_and_r_starts_it_again(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — escape_cancels_the_first_scan_and_r_starts_it_again()

fans out to 8 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

/// The mouse's back and forward buttons retrace the same history as
/// alt-arrows and the header buttons, and only on the explore screen.
#[gpui_kit::test]
fn mouse_side_buttons_go_back_and_forward(cx: &mut TestAppContext) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — mouse_side_buttons_go_back_and_forward()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

}

#[gpui_kit::test]
fn a_right_click_selects_the_tile_and_copies_its_path(cx: &mut TestAppContext) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — a_right_click_selects_the_tile_and_copies_its_path()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

/// Records land in `infos` as they are parsed, while other reads are
/// still on the disk: gathered into it after the last read, they cost a
/// tenth of a second on the way to the tree.
fn read_records(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — read_records()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

}

/// [`parse_record`] for a record whose update sequence is already undone.
fn parse_fixed(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — parse_fixed()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

}

#[test]
fn reparse_tag_precedence_preserves_directory_and_link_kinds() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — reparse_tag_precedence_preserves_directory_and_link_kinds()

fans out to 9 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

/// The last scan's tree brought up to date; `None` when a whole read is
/// needed instead. With it, where the next scan picks up, when that is
/// worth writing down: see [`RESAVE_JOURNAL`].
pub(super) fn resume(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — resume()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

}

fn scan_blocking(root: &Path, context: &Arc<WalkContext>) -> io::Result<Node> {
fn scan_blocking(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — scan_blocking()

fans out to 16 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@mgpai22
mgpai22 merged commit 24af733 into main Oct 3, 2026
7 of 9 checks passed
@mgpai22
mgpai22 deleted the context-menu branch October 3, 2026 05:42
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.

9 participants