stale-file: harden change detection beyond bare mtime equality #17

Closed
opened 2026-07-02 16:28:07 -04:00 by the_og · 0 comments
Owner

Problem

Follow-up from the supervisor review of PR #15 (stale-file protection, #4). The stale check compares disk_mtime != self._loaded_mtime (config_mtime returns Path.stat().st_mtime). Pure mtime equality can miss a concurrent external write when:

  • the external write lands within the filesystem's mtime resolution (coarse on some FS: FAT ~2s, older ext ~1s), or
  • a tool writes-then-restores content such that mtime is reset, or
  • clock/mtime is not monotonic across processes.

A missed detection means BCC silently overwrites an external edit (e.g. claude mcp add from a terminal) without showing the StaleDialog.

Not a data-loss emergency — write_config always backs up the pre-write file first, so the external version is recoverable from .bcc_backups/. But the whole point of #4 is to prompt before clobbering, and mtime alone can skip the prompt.

Proposed fix

Cheap hardening: pair mtime with file size (and optionally a content hash of just the server sections) captured at load and compared on save. disk_mtime != loaded_mtime OR disk_size != loaded_size catches the same-second/equal-mtime case at negligible cost. A full content hash is the most robust but heaviest; size+mtime is the pragmatic middle.

Acceptance criteria

  • Load-time snapshot records both mtime and size (extend config_mtime or add a sibling helper).
  • Save-time stale check triggers on a same-mtime-but-different-size external write.
  • Unit test: write a file, capture snapshot, rewrite with different content but force-set the original mtime (os.utime), assert the change is still detected.

Relevant code

  • bcc_core.py::config_mtime, external_change_summary
  • bcc.py::MainWindow._loaded_mtime, the stale check in the save path

Filed directly by the supervisor (Cowork) session.

## Problem Follow-up from the supervisor review of PR #15 (stale-file protection, #4). The stale check compares `disk_mtime != self._loaded_mtime` (`config_mtime` returns `Path.stat().st_mtime`). Pure mtime equality can miss a concurrent external write when: - the external write lands within the filesystem's mtime resolution (coarse on some FS: FAT ~2s, older ext ~1s), or - a tool writes-then-restores content such that mtime is reset, or - clock/mtime is not monotonic across processes. A missed detection means BCC silently overwrites an external edit (e.g. `claude mcp add` from a terminal) without showing the StaleDialog. Not a data-loss emergency — `write_config` always backs up the pre-write file first, so the external version is recoverable from `.bcc_backups/`. But the whole point of #4 is to *prompt* before clobbering, and mtime alone can skip the prompt. ## Proposed fix Cheap hardening: pair mtime with file **size** (and optionally a content hash of just the server sections) captured at load and compared on save. `disk_mtime != loaded_mtime OR disk_size != loaded_size` catches the same-second/equal-mtime case at negligible cost. A full content hash is the most robust but heaviest; size+mtime is the pragmatic middle. ## Acceptance criteria - Load-time snapshot records both mtime and size (extend `config_mtime` or add a sibling helper). - Save-time stale check triggers on a same-mtime-but-different-size external write. - Unit test: write a file, capture snapshot, rewrite with different content but force-set the original mtime (`os.utime`), assert the change is still detected. ## Relevant code - `bcc_core.py::config_mtime`, `external_change_summary` - `bcc.py::MainWindow._loaded_mtime`, the stale check in the save path _Filed directly by the supervisor (Cowork) session._
the_og added the P2 label 2026-07-02 16:28:07 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: the_og/better-claude-config#17