fix: surface settings persistence failures - #190
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks 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 |
|
@coderabbitai review |
|
|
|
||
| $effect(() => { | ||
| error = ""; | ||
| getSettings() |
There was a problem hiding this comment.
🟡 Unreadable settings stay silent
When settings-file reads fail, getSettings resolves to an offline default via get_settings. The new load error never appears.
Prompt for agents
The Settings.svelte load-error path depends on getSettings rejecting, but src-tauri/src/commands.rs get_settings converts every read_to_string error into Settings::default(). Distinguish a missing settings file, which can legitimately use the offline default, from permission and other I/O failures, which must propagate to the frontend so its actionable load error is displayed. Add backend and frontend integration coverage for both cases.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
확인했습니다. 이 finding은 유효합니다. commands::get_settings가 모든 read_to_string 실패를 offline default로 접어 UI의 load-error 경계를 무력화하고 있었습니다. Current Settings owner는 settings::load_settings에서 missing file만 first-run offline default로 허용하고, 기존 regular file의 UTF-8/I/O 실패는 path-free settings-read-failed, symlink/non-regular path는 settings-file-unsafe로 fail closed하도록 수정했습니다. Tauri handler도 settings::get_settings로 전환했습니다. 실제 temp filesystem에서 missing/default, invalid UTF-8/error, non-regular/symlink rejection을 검증하는 Rust regression tests를 추가했습니다. Exact head는 새 CI가 아직 terminal GREEN이 아니므로 thread는 그 evidence 전까지 열어 둡니다.
| } catch (error) { | ||
| checkbox.checked = persistedOnline; |
There was a problem hiding this comment.
🟡 Failed saves can erase prior setting
When set_settings fails after truncating the file (set_settings), persistOnlineToggle restores a value that is no longer persisted. Later operations silently use offline mode.
Prompt for agents
The frontend assumes a rejected set_settings leaves the previous durable value intact, but src-tauri/src/commands.rs writes settings.json in place. An error after opening the file can leave the old setting truncated or partially written. Persist through a temporary file in the same directory, flush as appropriate, and atomically replace settings.json only after the complete serialized value is written. Cover a failed replacement/write path and verify the previous file remains readable and unchanged.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
확인했습니다. 이 finding도 유효합니다. 기존 std::fs::write(settings.json, ...)는 대상 파일을 먼저 truncate하므로 실패한 save 뒤 frontend가 복원한 checkbox 값과 durable state가 달라질 수 있었습니다. Current Settings owner는 같은 디렉터리의 tempfile::NamedTempFile에 전체 JSON을 쓰고 flush+sync_all()한 뒤 persist()로 atomic replace하도록 바꿨습니다. 실제 filesystem에 기존 online_mode=true를 저장한 상태에서 replacement failure를 주입해 기존 bytes와 재로드 값이 그대로 유지되는 regression도 추가했습니다. tempfile의 current contract상 existing destination은 atomic replace되며 실패 시 original temp handle을 반환합니다. Exact-head hosted checks가 아직 GREEN이 아니므로 thread는 검증 완료 전까지 열어 둡니다.
Preserve the four-file Settings persistence/failure-feedback delta while adopting canonical #264 release and hosted-runner prerequisite changes through ancestry only. No release/workflow source is copied into the Settings owner. Signed-off-by: Seongho Bae <me@seonghobae.me>
Move the exposed settings persistence authority behind a tested same-directory temporary-file replacement. Missing settings remain safely offline, while unreadable or unsafe settings files now fail closed instead of masquerading as a successful default. Signed-off-by: Seongho Bae <me@seonghobae.me>
Expose the tested settings persistence boundary to the desktop UI instead of the legacy in-place command adapters. Signed-off-by: Seongho Bae <me@seonghobae.me>
Purpose
Make Settings persistence failures visible and fail closed without changing DiskSage's offline-by-default authority. A checkbox must never look persisted when the backend save failed.
Current authority — 2026-09-05 KST
main:0e90f9cebadbd7f59606baaec4ca1d2f178c899a;3e33229b4157a084a1985a1c7e952f98a64f25e3;dc02de371d7535bee0d2730c89314c876885a12f;Backend/UI contract
The exposed Tauri boundary routes through the canonical
settingsowner.load_settingstreats onlyNotFoundas first-run offline state, fails closed for unreadable existing files, and rejects symlink/non-regular settings paths.save_settingswrites complete JSON to a same-directory temporary file, flushes and syncs it, then atomically replaces the destination. The UI adopts visible/application state only from a successful persisted response, announces bounded errors, and blocks repeat input while persistence is in flight.Real temporary-filesystem regressions prove missing-file defaulting, invalid UTF-8 read failure, non-regular rejection, Unix symlink rejection, successful replacement, and preservation of prior bytes/value when replacement fails. This is replacement evidence, not a claim of containing-directory power-loss durability or ancestor-replacement authority. #344 provides a reusable Unix object-bound publication pattern, but adoption into Settings remains owner-specific rather than assumed.
commands.rsstill contains legacy unregistered get/set adapters inherited by the broader command module; remove/delegate those in the commands owner rather than broadening this Settings PR.Required before integration
Keep Draft until #264 is integrated or equivalently present on protected lineage and one unchanged exact #190 head satisfies every applicable native/central Test/Release/Security/SAST/CodeQL/OSV/Scorecard/review/ruleset gate plus repository-wide exact owned-production coverage through #337. No self-approval, force-push, destructive rebase, gate weakening, administrative bypass, or predecessor-evidence transfer.