fix(reset): release log file handle + queue locked entries on Windows reboot#2668
fix(reset): release log file handle + queue locked entries on Windows reboot#2668YOMXXX wants to merge 5 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR makes the rolling-log guard droppable mid-process, adds a Windows-only reboot-deletion scheduler that queues files/dirs for deletion at next boot, and wires both into Tauri's reset_local_data so locked-path delete failures either schedule reboot deletions with precise diagnostics or report the original lock cause. ChangesWindows Cleanup and File-Lock Handling
Sequence Diagram(s)sequenceDiagram
participant TauriReset
participant EmbeddedCore
participant Logging
participant RebootScheduler
participant Windows
TauriReset->>EmbeddedCore: stop core
TauriReset->>Logging: shutdown_file_guard()
Logging->>Windows: close rolling-log handle
TauriReset->>TauriReset: remove directories
TauriReset->>RebootScheduler: schedule_path_for_reboot_deletion (on lock error)
RebootScheduler->>Windows: MoveFileExW(..., MOVEFILE_DELAY_UNTIL_REBOOT)
Windows-->>RebootScheduler: success/failure
RebootScheduler-->>TauriReset: RebootDeletionSchedule or error
TauriReset->>TauriReset: format user diagnostic
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src-tauri/src/lib.rs`:
- Around line 530-532: The log call in reset_local_data uses a mismatched format
string ("... at {}: {schedule_err}") but doesn't supply the value for the {}
placeholder; update the log::error! invocation (the one referencing label and
schedule_err) to include the path's display value by passing path.display() as
the missing argument so the format string is satisfied (e.g., include
path.display() alongside label and schedule_err).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 43e56a9d-3164-4aaa-b339-9fcec675522d
📒 Files selected for processing (3)
app/src-tauri/src/lib.rsapp/src-tauri/src/reset_reboot_schedule.rssrc/core/logging.rs
The Windows-only `schedule_reboot_delete_or_describe` error branch
included a positional `{}` placeholder without a corresponding
argument, which would have failed to compile when targeting Windows.
The Linux/macOS `cargo check` runs skipped this code path under
`#[cfg(target_os = "windows")]`, so the regression slipped past local
verification.
Per CodeRabbit review on PR tinyhumansai#2668.
|
Pushed empty commit 4aefaac to retrigger CI after |
|
Fixed the Rust core test flake seen in |
|
Latest head 5966d29 is green now. CodeRabbit approved, no unresolved review threads, and all CI checks are passing.\n\nFollow-ups handled while babysitting:\n- fixed the Windows-only log format argument in 1da4ac8\n- retriggered CI after a runner disk exhaustion failure\n- fixed the unrelated Rust tool-memory test flake in 5966d29 by avoiding UUID-derived tool names that can trip the PII guard\n\n@graycyrus could you take another look when you have a chance? |
M3gA-Mind
left a comment
There was a problem hiding this comment.
Good fix overall — root cause analysis is correct and the two-layer approach (release own handle first, reboot-schedule fallback second) is sound. A few things to address before merge:
Merge conflict
The PR currently has a merge conflict with main (mergeStateStatus: DIRTY). Please rebase on latest main and resolve before this can land.
Issues
Bug (docs) — reset_reboot_schedule.rs module comment
The module-level doc at line 23–27 says:
"scheduling a delete-on-reboot for a file the calling user already owns does not require elevation"
This contradicts the PR body, the lib.rs comments, and Microsoft's own docs — MoveFileExW(..., DELAY_UNTIL_REBOOT) writes to HKLM\...\PendingFileRenameOperations and does require admin rights on current Windows versions. The code handles the non-admin failure path correctly; the comment just needs to match reality. Suggest: "may fail for non-administrator users; the caller handles the failure path gracefully."
Minor — logging.rs test races on global state
shutdown_file_guard_takes_installed_guard mutates the process-global FILE_GUARD without a serialization lock. Other tests in the same binary that touch init_for_embedded run in parallel threads and could race. reset_reboot_schedule::tests avoids this correctly with static SCHEDULE_LOCK: Mutex<()>. Add the same pattern here.
Minor — no is_absolute() guard in schedule_one
MoveFileExW + DELAY_UNTIL_REBOOT paths must be absolute (relative paths are ambiguous at boot time). Data dirs are always absolute in practice, but a debug_assert!(path.is_absolute()) in schedule_one would catch any future regression cheaply.
Minor — partial walk on first error not reflected in error message
schedule_inner returns on the first io::Result::Err, so RebootDeletionSchedule counts may be a partial tally when a mid-walk permission error occurs. The success message reports "N files and M folders queued" without indicating whether the walk completed — could mislead support diagnostics. Low priority, but worth a note or a partial-walk indicator.
Nit — unrelated change in tool_memory.rs
The unique_tool_name AtomicUsize refactor is fine but orthogonal to this bug fix. Consider a separate commit or PR to keep the diff focused.
What's good
- The root cause (host-process
WorkerGuardsurvivingCoreProcessHandle::shutdown) is correctly identified and fixed. Mutex<Option<WorkerGuard>>→shutdown_file_guard()is the minimal correct refactor.- Symlink handling in
schedule_inneris correct —DirEntry::metadata()does not follow symlinks, consistent with the top-levelsymlink_metadatacall. - Depth-first ordering (children before parent) is required for the Windows session manager; it's implemented correctly.
- Test coverage covers happy path, single file, empty dir, missing path, and both the admin/non-admin branches of the error formatter.
cfgguards are applied consistently; non-Windows builds are unaffected.
Fix the conflict + the doc comment and this is ready.
The Windows-only `schedule_reboot_delete_or_describe` error branch
included a positional `{}` placeholder without a corresponding
argument, which would have failed to compile when targeting Windows.
The Linux/macOS `cargo check` runs skipped this code path under
`#[cfg(target_os = "windows")]`, so the regression slipped past local
verification.
Per CodeRabbit review on PR tinyhumansai#2668.
…partial schedule) Per M3gA-Mind's review on PR tinyhumansai#2668: * `reset_reboot_schedule.rs` module doc — correct the privileges claim. `MoveFileExW + MOVEFILE_DELAY_UNTIL_REBOOT` writes to HKLM, so the call may fail for non-administrator users; rewrite the comment to match Microsoft's docs and point at the lib.rs failure-path handling. * `RebootDeletionSchedule` — add `partial: bool` and pair the `Err` variant with a `RebootDeletionFailure { error, partial }` carrying what was queued before the walk aborted. Support logs and the user-facing message now distinguish "we scheduled some of the tree but not all" from "scheduling never started." `lib.rs` consumes the partial count both in the log line and in the user copy. * `schedule_one` — `debug_assert!(path.is_absolute())` catches future regressions where a caller hands MoveFileExW a relative path the session manager could not resolve at boot. * `shutdown_file_guard_takes_installed_guard` — add a dedicated `FILE_GUARD_LOCK` so concurrent tests touching `FILE_GUARD` cannot race, mirroring the `SCHEDULE_LOCK` pattern in `reset_reboot_schedule::tests`. Also rebased onto the latest upstream/main to clear the merge conflict the PR had picked up. Refs tinyhumansai#1615
5966d29 to
eadef1e
Compare
|
@M3gA-Mind thanks for the thorough review — all 5 items addressed in eadef1e (also rebased onto the latest 1. Merge conflict — rebased; PR is now clean against current 2. Doc bug (
3. Test race on 4. 5. Partial-walk indicator — went a step further than just a note: added a new 6. PII test ( Validation locally on macOS:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src-tauri/src/lib.rs`:
- Around line 529-552: In reset_local_data, when handling the Err(failure) from
schedule_path_for_reboot_deletion, special-case the case where
failure.error.kind() == std::io::ErrorKind::NotFound and failure.partial.total()
== 0 and treat it as a success/no-op instead of formatting a hard failure
message; implement this by checking failure.error.kind() and partial_total early
in the Err(failure) arm (using the existing failure.error and failure.partial
identifiers) and returning the successful/no-op result (or skipping the
error-toast path) before emitting the error log and the formatted failure
strings.
In `@app/src-tauri/src/reset_reboot_schedule.rs`:
- Around line 190-255: Tests that call schedule_path_for_reboot_deletion
currently assert success (expect("schedule")) but fail in non-elevated
environments because MoveFileExW(MOVEFILE_DELAY_UNTIL_REBOOT) (invoked via
schedule_one) cannot write the PendingFileRenameOperations registry; update the
tests to not require actual scheduling: change calls to
schedule_path_for_reboot_deletion in tests (e.g.,
schedule_walks_files_then_dirs, schedule_single_file_reports_one_file,
schedule_empty_dir_counts_one_dir) to accept the permission/registry failure
path the runtime code already handles by either (a) injecting a test seam/mocked
schedule_one that returns a successful traversal-only RebootDeletionSchedule, or
(b) asserting that the result is an Err whose partial RebootDeletionSchedule
contains the expected files/dirs counts when the underlying error.kind() ==
io::ErrorKind::PermissionDenied (or the specific registry/MoveFileExW failure);
implement the mock or conditional assertion so tests validate traversal/counting
without requiring MoveFileExW privileges.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e7a801f3-929a-42e6-8a10-1641bad8464b
📒 Files selected for processing (3)
app/src-tauri/src/lib.rsapp/src-tauri/src/reset_reboot_schedule.rssrc/core/logging.rs
…FileExW privileges Two issues from CodeRabbit's review on PR tinyhumansai#2668: 1. `reset_local_data_delete_error` now returns `Result<(), String>`. When the locked path disappears between the failed `remove_*` call and the reboot-fallback walk (race condition — whoever held the handle finished cleaning up first), the scheduler reports `NotFound` with an empty partial schedule. That is "already deleted", not a failure, so the helper returns `Ok(())` and the caller skips the error toast instead of surfacing "couldn't remove (it's not there anyway)" copy. 2. `schedule_path_for_reboot_deletion` now delegates to `schedule_path_with_scheduler(path, &mut scheduler)`. The public entry point still wires in the real `MoveFileExW + DELAY_UNTIL_REBOOT` step; the unit tests inject a no-op `FnMut(&Path) -> io::Result<()>` so the traversal/counting logic runs on every dev machine (administrator or not), validating the depth-first ordering and the partial-schedule semantics without ever touching the HKLM `PendingFileRenameOperations` registry key. Also adds a focused test that simulates a non-admin MoveFileExW failure and asserts the partial counts reach the caller intact. Refs tinyhumansai#1615.
|
@coderabbitai both Major items addressed in 9d96612: 1. Changed 2. Introduced an internal seam The new tests also capture the call order against the stub, so a regression in depth-first ordering (children before their parent dir) would surface as a path-sequence mismatch even though the OS-side scheduling stays out of the test process. Added Local validation: |
|
Tip For best results, initiate chat on the files or code changes.
The implementation looks clean. 🐇 ✨ If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src-tauri/src/lib.rs (1)
394-395:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftRe-arm file logging after dropping the guard.
shutdown_file_guard()disables the host file appender for the rest of this Tauri process, so everylog::*call after this point—including the new reboot-schedule counts and fallback errors below—missesopenhuman-YYYY-MM-DD.log. That means the support diagnostics added in this PR disappear right after the first reset. Please either reopen a fresh file guard before returning or route the reset-path diagnostics to a durable sink that survives the guard drop.As per coding guidelines, "Log entry/exit, branches, external calls, retries/timeouts, state transitions, and errors with verbose diagnostics using stable grep-friendly prefixes and correlation fields".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src-tauri/src/lib.rs` around lines 394 - 395, The shutdown_file_guard() call in reset_local_data drops the file-appender guard (log_guard_dropped) so subsequent log::* calls no longer reach the daily openhuman-YYYY-MM-DD.log; reopen a fresh file guard or switch the reset diagnostics to a durable sink before returning: after calling openhuman_core::core::logging::shutdown_file_guard() (and using log_guard_dropped) immediately call your logging library's corresponding "open/reopen file guard" function (or create and install a new file guard object) so that further logging in reset_local_data and the reboot/fallback paths is written to the file, or alternatively send those specific messages to a durable sink (e.g., explicit file write or stderr/syslog) if reopening is not desired; make the change in reset_local_data around the shutdown_file_guard() usage so the sequence ensures file logging is re-enabled for the later log statements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@app/src-tauri/src/lib.rs`:
- Around line 394-395: The shutdown_file_guard() call in reset_local_data drops
the file-appender guard (log_guard_dropped) so subsequent log::* calls no longer
reach the daily openhuman-YYYY-MM-DD.log; reopen a fresh file guard or switch
the reset diagnostics to a durable sink before returning: after calling
openhuman_core::core::logging::shutdown_file_guard() (and using
log_guard_dropped) immediately call your logging library's corresponding
"open/reopen file guard" function (or create and install a new file guard
object) so that further logging in reset_local_data and the reboot/fallback
paths is written to the file, or alternatively send those specific messages to a
durable sink (e.g., explicit file write or stderr/syslog) if reopening is not
desired; make the change in reset_local_data around the shutdown_file_guard()
usage so the sequence ensures file logging is re-enabled for the later log
statements.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d5168826-c94e-4ea0-9a05-dffda2ead287
📒 Files selected for processing (2)
app/src-tauri/src/lib.rsapp/src-tauri/src/reset_reboot_schedule.rs
There was a problem hiding this comment.
Review — Cyrus Grey
Solid fix for #1615. Two-part approach is exactly right: close your own handles first, then fall back to reboot scheduling for foreign locks.
What I liked:
OnceLock→Mutex<Option<_>>conversion inFILE_GUARDis clean. Guard drops while the mutex is still held, no race window forinit_for_embeddedto sneak in.- Test seam via
schedule_path_with_scheduler+ injectableFnMutis the right pattern — exercises traversal/counting without needing admin rights or real boot-time scheduling. - Symlink guard in
schedule_innerprevents escaping.openhumanvia directory symlinks. Good catch. - The
NotFound+ empty partial race condition handler (schedule_reboot_delete_or_describe) is well-reasoned — if the path vanished between the lock failure and the reboot walk, the reset goal is already met. - Error messages are actionable with concrete counts ("142 files / 14 dirs queued") instead of the old generic "close all windows" advice.
Minor notes (non-blocking):
saturating_addonfiles/dirscaps atu32::MAXsilently, but.openhumanwill never hit that — fine as-is.- After
shutdown_file_guard(), subsequent file-layer log records are silently discarded until restart. Documented and intentional — just flagging for anyone reading the diff later.
All CodeRabbit findings already addressed in 9d96612. No new issues from my end.
… reboot `reset_local_data` already stops the embedded core (drops every SQLite / log handle owned by the core tokio task) before walking the workspace, but Windows users still report `ERROR_SHARING_VIOLATION` (os error 32) when removing the data directory — see tinyhumansai#1615 follow-up. Root cause: the rolling `openhuman-YYYY-MM-DD.log` appender is owned by the Tauri host process, not by the core task, so `CoreProcessHandle::shutdown` does not release its OS file handle. The host keeps `<data_dir>/logs/` locked while `remove_dir_all` runs. Two fixes: 1. `core::logging::shutdown_file_guard()` — take and drop the rolling writer's `WorkerGuard`. The non-blocking background thread exits and the log file handle is released. Called between the core shutdown and the directory removal in `reset_local_data`. 2. `reset_reboot_schedule` (Windows-only) — when an external process (anti-virus / indexer / sibling app) still holds a handle in the tree, walk it depth-first and queue every file and then every (now-empty) directory for deletion via `MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)`. The user message now reports `<files> files and <dirs> folders ... queued for deletion the next time you restart Windows` instead of a generic "close OpenHuman windows" instruction the user already tried. The reboot fallback's `MoveFileExW` step needs the calling user to belong to the Administrators group (it writes a HKLM session-manager key); if the call fails we still surface the original lock error plus the schedule failure so the support log captures both. Includes unit tests covering the guard drop, the depth-first walk, the single-file / missing-path / empty-dir cases, and the reset error formatter going through both the reboot-scheduled and reboot-failed branches.
The Windows-only `schedule_reboot_delete_or_describe` error branch
included a positional `{}` placeholder without a corresponding
argument, which would have failed to compile when targeting Windows.
The Linux/macOS `cargo check` runs skipped this code path under
`#[cfg(target_os = "windows")]`, so the regression slipped past local
verification.
Per CodeRabbit review on PR tinyhumansai#2668.
…partial schedule) Per M3gA-Mind's review on PR tinyhumansai#2668: * `reset_reboot_schedule.rs` module doc — correct the privileges claim. `MoveFileExW + MOVEFILE_DELAY_UNTIL_REBOOT` writes to HKLM, so the call may fail for non-administrator users; rewrite the comment to match Microsoft's docs and point at the lib.rs failure-path handling. * `RebootDeletionSchedule` — add `partial: bool` and pair the `Err` variant with a `RebootDeletionFailure { error, partial }` carrying what was queued before the walk aborted. Support logs and the user-facing message now distinguish "we scheduled some of the tree but not all" from "scheduling never started." `lib.rs` consumes the partial count both in the log line and in the user copy. * `schedule_one` — `debug_assert!(path.is_absolute())` catches future regressions where a caller hands MoveFileExW a relative path the session manager could not resolve at boot. * `shutdown_file_guard_takes_installed_guard` — add a dedicated `FILE_GUARD_LOCK` so concurrent tests touching `FILE_GUARD` cannot race, mirroring the `SCHEDULE_LOCK` pattern in `reset_reboot_schedule::tests`. Also rebased onto the latest upstream/main to clear the merge conflict the PR had picked up. Refs tinyhumansai#1615
…FileExW privileges Two issues from CodeRabbit's review on PR tinyhumansai#2668: 1. `reset_local_data_delete_error` now returns `Result<(), String>`. When the locked path disappears between the failed `remove_*` call and the reboot-fallback walk (race condition — whoever held the handle finished cleaning up first), the scheduler reports `NotFound` with an empty partial schedule. That is "already deleted", not a failure, so the helper returns `Ok(())` and the caller skips the error toast instead of surfacing "couldn't remove (it's not there anyway)" copy. 2. `schedule_path_for_reboot_deletion` now delegates to `schedule_path_with_scheduler(path, &mut scheduler)`. The public entry point still wires in the real `MoveFileExW + DELAY_UNTIL_REBOOT` step; the unit tests inject a no-op `FnMut(&Path) -> io::Result<()>` so the traversal/counting logic runs on every dev machine (administrator or not), validating the depth-first ordering and the partial-schedule semantics without ever touching the HKLM `PendingFileRenameOperations` registry key. Also adds a focused test that simulates a non-admin MoveFileExW failure and asserts the partial counts reach the caller intact. Refs tinyhumansai#1615.
9d96612 to
2f26830
Compare
|
@graycyrus — same situation as #2654: the two red CI checks on this PR are both pre-existing flakes on 1. Markdown Link Check — external URL flakeJob run — lychee reports: DigitalOcean's status page was returning 521 (Cloudflare "web server down") at scan time. The URL is in 2. test / Rust Core Tests + Quality — known main-side flakeJob run — the only failed test is:
Same test has been flaking on Re-run links if helpful:
|
Summary
<data_dir>/logs/openhuman-*.log) beforeremove_dir_allruns — the real reasonreset_local_datastill hit Windows file locks even after the embedded core was shut down.MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT).Problem
#1615 has been open since 1 Sentry-traced report and is the bug that triggers
Failed to remove C:\Users\<user>\.openhuman: 另一个程序正在使用此文件,进程无法访问 (os error 32)on Windows. PR #2395 / #1811 surfaced a clearer "close all OpenHuman windows" prompt and pushed core shutdown ahead of the removal, but reports keep coming and the issue is still tracked as having two suggested fixes outstanding:The remaining-handle problem turned out to be subtle.
CoreProcessHandle::shutdownalready drops the embedded core's tokio task, which RAII-drops every SQLite connection and writer inside<data_dir>. But the daily-rolling log appender (tracing_appender::rolling::*) is installed bycore::logging::init_for_embeddedand parked in a process-wideOnceLock<WorkerGuard>— that guard lives in the Tauri host process, not in the core task, so it survives the core shutdown and keepsopenhuman-YYYY-MM-DD.logopen. Windows then refuses to delete the parent<data_dir>/logs/directory underneath it.Solution
1. Release the host-side log file handle (the actual fix)
src/core/logging.rs:FILE_GUARDfromOnceLock<WorkerGuard>toMutex<Option<WorkerGuard>>so it can be taken at runtime instead of only at process exit.pub fn shutdown_file_guard() -> boolthat swaps out the guard, drops it, and reports whether anything was actually released. Dropping the guard exits the non-blocking writer's background thread, flushes pending records, and closes the OS file handle.app/src-tauri/src/lib.rs::reset_local_data:shutdown_file_guard()betweenCoreProcessHandle::shutdown().await(step 3) and theremove_dir_allwalk (step 4). After this, no part of the OpenHuman process tree holds a handle inside<data_dir>.The stderr layer and Sentry layer keep working — they don't keep files open. The
tracingsubscriber itself stays installed (the globaltry_initcannot be replaced safely from inside the process), so any record written to the file layer after the guard drops is silently discarded. That is intentional and documented onshutdown_file_guard: after a reset the user is expected to restart the app soon, and discarding the in-flight records is preferable to either holding the file open or panicking.2. Schedule still-locked entries for reboot deletion (the belt-and-suspenders fallback)
New module
app/src-tauri/src/reset_reboot_schedule.rs(Windows-only via#![cfg(target_os = \"windows\")]):schedule_path_for_reboot_deletion(path)walkspathdepth-first. For each child it callsMoveFileExW(child, NULL, MOVEFILE_DELAY_UNTIL_REBOOT). Directories are scheduled only after their contents have been queued — the Windows session manager requires directories to be empty at boot-time..openhumancannot accidentally schedule unrelated paths for deletion.RebootDeletionSchedule { files, dirs }count so the support log records what was actually queued.reset_local_data_delete_errornow consults this fallback when it seesERROR_SHARING_VIOLATION/ERROR_LOCK_VIOLATION(os error 32/33):"Couldn't remove ... right now ... <N> files and <M> folders have been queued for deletion the next time you restart Windows — restart soon to finish the reset."The reboot fallback requires the calling user to be in the Administrators group —
MoveFileExW(..., DELAY_UNTIL_REBOOT)writes to a HKLM session-manager key and Microsoft documents the elevation requirement. For non-admin users the message degrades gracefully to the failure path above, which still names a concrete next step.Submission Checklist
core::logging::tests,testsof the Tauri shell, andreset_reboot_schedule::tests.shutdown_file_guard, the depth-first walk, the single-file / empty-dir / missing-path cases, and the reset error formatter going through both the reboot-scheduled and reboot-failed branches.docs/TEST-COVERAGE-MATRIX.md.## Related— N/A: no feature matrix row.docs/RELEASE-MANUAL-SMOKE.md) — the Settings → Reset Local Data path is already on the manual smoke list; the visible change here is the error wording (now reports queued counts on lock failures).Refs(notCloses— see Related)Impact
remove_dir_allwould have walked.MoveFileExW(.., NULL, DELAY_UNTIL_REBOOT)is well-documented Windows API. The schedule is per-user data inside.openhumanand the calling user already had unrestricted access to it.core::logging::shutdown_file_guardis a new public function. No existing callers change behaviour —log_directory()andinit_for_embeddedstill work exactly as before.Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
Validation Blocked
Behavior Changes
Parity Contract
Duplicate / Superseded PR Handling
Summary by CodeRabbit