package backup // R-49, operator ruling D21 = B (2026-10-22): a template may mark a named volume `excluded` (a pure cache the app // rebuilds by itself — Immich's machine-learning model cache, 62 % of its off-site copy). Such a volume is never // dumped, so it is in no recovery unit: not on the drive's copy, not on the second drive, not off-site. Its last tar // is removed by DumpAppVolumes' stale-tar clean-up. A restore then finds no tar for it and leaves the volume as the // app makes it (Immich downloads its models again). The rule and its guards: appbackup.ExcludedDockerVolumes. // volumeExclusionProvider is implemented by the production stack adapter (cmd/controller); test fakes need not. type volumeExclusionProvider interface { GetExcludedDockerVolumes(name string) []string } // dumpableVolumes is the stack's named Docker volumes minus the ones its template excludes. func (m *Manager) dumpableVolumes(stackName string) []string { all := m.stackProvider.GetDockerVolumes(stackName) ex, ok := m.stackProvider.(volumeExclusionProvider) if !ok || len(all) == 0 { return all } skip := map[string]bool{} for _, v := range ex.GetExcludedDockerVolumes(stackName) { skip[v] = true } if len(skip) == 0 { return all } out := make([]string, 0, len(all)) for _, v := range all { if skip[v] { m.logger.Printf("[INFO] [backup] Volume %s/%s is excluded by the template (a cache the app rebuilds) — not dumped (R-49)", stackName, v) continue } out = append(out, v) } return out }