Refinement Audit — Cycle 4
Refinement Audit — Cycle 4
Summary
The project is in better shape after cycle 3; the remaining issues are concentrated in boundary enforcement and publish-time repository safety rather than broad structural defects. This cycle found three new problems in files that were not part of prior audits: one git-state bug that can break branch-switch publishes, plus two contract-validation gaps in config persistence and shell path handling.
Critical Findings
None.
Important Findings
I1: Auto-stash omits untracked files before branch-switch publishes
- File:
src/main/services/git/GitService.ts:180 - Issue: The stash helper uses
git stash push -mwithout including untracked files. During publish, the caller treats any dirty worktree as stashable before checking out the target branch, so untracked documents or sidecars can still block checkout or remain behind on the original branch. - Evidence:
PublishServicedecides to stash whenevergit.status().isClean()is false atsrc/main/services/git/PublishService.ts:348, then callsgit.stash(...)before branch checkout. ButGitService.stashexecutesthis.git.stash(['push', '-m', message])atsrc/main/services/git/GitService.ts:184, which does not include untracked paths. - Fix: Include
--include-untracked(or-u) in the stash command and add regression coverage that verifies branch-switch publishes stash untracked files too.
I2: ConfigService.save() still strips unknown keys and reports success
- File:
src/main/services/config/ConfigService.ts:411 - Issue: Direct callers of
ConfigService.save()can pass objects with unsupported fields and receive a successful save even though those fields are silently dropped. That leaves persistence semantics inconsistent with the stricter update contract added last cycle. - Evidence: The save path validates with
parseAppConfig(config as unknown, { unknownKeyPolicy: 'strip' })atsrc/main/services/config/ConfigService.ts:411, then writesroundTrip.configto disk. Unsupported keys are removed instead of causingCONFIG_CORRUPT. - Fix: Validate
save()input withunknownKeyPolicy: 'reject'and add a regression test that an extra field is rejected instead of silently stripped.
I3: shell:openPath accepts relative and malformed paths at the main-process boundary
- File:
src/main/ipc/handlers/utilityHandlers.ts:60 - Issue: The handler only checks that
raw.pathis a non-empty string before delegating toshell.openPath. A compromised or buggy renderer can send cwd-relative or malformed path strings and the main process will forward them to the OS shell. - Evidence: After trimming, the handler passes
pathstraight intoshell.openPath(path)atsrc/main/ipc/handlers/utilityHandlers.ts:67with no absolute-path or control-character validation. - Fix: Reject non-absolute or control-character-containing paths before calling Electron shell, and add direct handler tests for the new validation path.
Minor Findings
None.
Design Improvements
None.
Regressions
None detected.
TOTAL_FINDINGS: 3