internal/watcher/CLAUDE.md
Rama main · réplica de lectura del repositorio original.
# internal/watcher
## Purpose
A simple cross-platform config-file watcher based on `os.Stat` polling rather than inotify. Polling works reliably where inotify does not: Docker single-file bind mounts and k8s ConfigMap symlink projections (atomically swapped targets).
> Note: the directory is `internal/watcher` but the Go package is named **`configwatcher`**.
## Key files
| File | Role |
|---|---|
| `watcher.go` | The whole package: the `Watcher`, its `Run` poll loop, and the `stat`/`changed` helpers. |
## Important types & functions
- `Watcher` struct — `watcher.go`. Fields: `Path`, `Interval`, and `OnChange func()` callback.
- `DefaultInterval` — `watcher.go`. 2 seconds; used when `Interval <= 0`.
- `(*Watcher) Run(ctx context.Context)` — `watcher.go`. Blocks until `ctx` is canceled; polls on a ticker and invokes `OnChange` on each detected change. The baseline poll establishes initial state and does **not** fire `OnChange`.
- `stat(path) snapshot` — `watcher.go`. Reads existence/modtime/size; logs unexpected stat errors but treats `ErrNotExist` as simply "missing".
- `changed(prev, cur) bool` — `watcher.go`. Fires on modtime or size change, and on missing→present (file reappears). Stays quiet on present→missing (treats a vanished file as a transient rename-style write).
## Connections
Standard-library-only leaf package. Consumed by the application entry/config layer (`cmd/quartermaster/quartermaster.go` / `internal/config`) to trigger config reloads; the `OnChange` callback typically emits a `ConfigFileChangedEvent` (see `internal/shared/events.go`).
## Gotchas
- Change detection is modtime+size based; an in-place edit that preserves both is not detected.
- Present→missing deliberately does not fire, so callers won't see a reload on a brief delete during atomic rewrites — the subsequent missing→present transition fires instead.