Skip to content

feat: sync filters UI for issue #1#2

Merged
psimaker merged 9 commits into
mainfrom
feat/ignore-patterns
May 9, 2026
Merged

feat: sync filters UI for issue #1#2
psimaker merged 9 commits into
mainfrom
feat/ignore-patterns

Conversation

@psimaker

@psimaker psimaker commented May 9, 2026

Copy link
Copy Markdown
Owner

Closes #1.

Adds a per-vault Sync Filters UI on top of the existing GetFolderIgnores / SetFolderIgnores bridge functions. Users can toggle preset ignore-pattern groups in plain language, edit custom patterns, see a vault scan that surfaces actual byte sizes of heavy folders, and skip conflicting files in one tap from the conflict resolver.

What's in

Bridge

  • ScanFolderForKnownPatterns (Go) — checks the sync folder root and one level deep into each non-hidden subdirectory, aggregates matches per pattern (designed for "Obsidian root + multiple vaults as subdirs" layouts). Detects .git, .copilot-index, node_modules, .obsidian/cache. 7 unit tests including nested-vault and hidden-subdir cases.

Models & service

  • IgnorePreset catalog: Workspace state, Trash, Git repository, macOS metadata, Copilot index, Obsidian app cache. Recommended set (Workspace + Trash) is silently applied to new folders.
  • SyncthingManager filter API: ignorePatterns, setIgnorePatterns, togglePreset, addIgnorePattern, scanFolderForKnownPatterns, plus per-folder UserDefaults flag for the first-run sheet.
  • Existing 3 silent default patterns migrate automatically: derived state shows Workspace + Trash as ON; nothing on disk changes.

UI

  • IgnorePatternsView (Recommended / Found in this vault / Other presets / Custom patterns / footer link to Syncthing pattern docs).
  • SyncFilterRecommendationSheet shown once per vault on first detail-view open; pre-checks Recommended + auto-checks anything heavy the scan found.
  • ContentView: NavigationLink between Conflicts and Shared With sections.
  • ConflictDiffView: top-bar menu with Always skip on this iPhone (adds the conflict's exact relative path to the folder's ignore list).

Localization

en, de, zh-Hans for all new strings.

Pattern semantics — verified

Confirmed via lib/ignore/ignore.go:474-488: Syncthing auto-expands every unanchored pattern to also match at any depth. So .git, .obsidian/workspace.json, .Trash etc. correctly match in deeply nested vault subdirectories without needing a **/ prefix.

Spec

Full UX spec lives in docs/sync-filters-ux.md (mockups, preset catalog, migration plan, multi-vault behavior).

Tests

  • Go: full bridge regression (~40s) including 7 new scanner tests
  • Swift: 12 new unit tests (IgnorePreset catalog + recommendation-sheet flag)
  • Manual smoke test on physical iPhone with multi-vault Obsidian setup

Sync Filters UI for per-vault ignore patterns

Adds a user-facing "Sync Filters" UI to manage .stignore patterns per vault, allowing users to exclude heavy or irrelevant folders (.git, node_modules, .obsidian/cache, etc.) from syncing without manually editing configuration files.

User-visible features

  • Sync Filters screen: New "Sync Filters" link in vault detail view. Displays five sections: Recommended presets (Workspace state, Trash), detected patterns found by folder scanning with sizes/file counts, other available presets (Git, macOS metadata, Copilot index, Obsidian cache), custom pattern editor (add/delete), and link to Syncthing docs.

  • First-run recommendation sheet: Shown once per vault (tracked via UserDefaults). Auto-detects heavy folders and pre-checks recommended presets; users can toggle selections and apply or skip.

  • Conflict resolution action: New "Always skip on this iPhone" toolbar action in ConflictDiffView that adds a conflict path to the folder's ignore list with undoable confirmation.

  • Smart defaults: New vaults silently apply Workspace + Trash presets by default. Existing vaults preserve their current .stignore without overwriting on app update.

  • Localization: All new UI strings translated to English, German, and Simplified Chinese.

Technical implementation

  • Go bridge: New ScanFolderForKnownPatterns() function scans vault root and one directory level deep (skipping hidden dirs), totals file sizes and counts, and returns detected patterns as JSON.

  • Pattern semantics: Uses Syncthing-compatible patterns that auto-expand unanchored patterns to match at any depth, ensuring consistency with Syncthing's native behavior.

  • Safe read-modify-write: .stignore reads are guarded against malformed files; writes abort with SyncUserError on decode failure to prevent silent overwrites.

  • Preset management: IgnorePreset catalog maps preset IDs to pattern groups; toggle operations preserve user-added custom patterns and handle deselection-aware merges.

Testing

  • Go: 7 new scanner unit tests covering .git detection, empty vaults, multiple candidates, nested vaults, hidden directories, and empty directories. Full bridge regression suite (~40s).
  • Swift: 12 new unit tests for preset catalog integrity, recommendation-sheet state tracking, and per-folder persistence.
  • Manual: Multi-vault smoke test performed.

Privacy/security & background execution

No background execution changes. Folder scanning happens only on-demand when the Sync Filters sheet is presented. No privacy impact; scanning is local to the vault and detection results are stored only in memory or UserDefaults for the "shown" flag.

Tentative design for the ignore-patterns UI promised in issue #1.
Captures preset catalog, first-run sheet, conflict→ignore button,
and migration plan for the 3 silent defaults.

Open for vitaly74's input before implementation begins.
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 581a84a3-81a4-48c5-92ee-1cb624b65065

📥 Commits

Reviewing files that changed from the base of the PR and between ad24643 and 703471b.

📒 Files selected for processing (5)
  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
✅ Files skipped from review due to trivial changes (3)
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/de.lproj/Localizable.strings
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
ios/**/*.swift

📄 CodeRabbit inference engine (README.md)

Follow Swift API Design Guidelines

Use Swift strict concurrency where applicable

Support iOS background execution via BGAppRefreshTask and BGContinuedProcessingTask (iOS 26+ when available)

Include VoiceOver and Dynamic Type accessibility support throughout the app

Use SwiftUI for iOS UI implementation

Use APNs silent notifications via Cloud Relay for push wake-ups

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift

⚙️ CodeRabbit configuration file

ios/**/*.swift: Focus on Swift 6 strict concurrency, Sendable/MainActor correctness, Task cancellation,
retain cycles, memory pressure, SwiftUI observation state, StoreKit/APNs flows, and iOS background execution limits.
Pay special attention to BGAppRefreshTask and BGContinuedProcessingTask behavior, expiration handling,
bounded work, and cleanup when the app is suspended or terminated.

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
**/*

⚙️ CodeRabbit configuration file

**/*: VaultSync syncs private Obsidian notes through Syncthing. Treat data loss,
privacy leaks, security regressions, and broken sync behavior as high priority.
Do not nitpick formatting unless it affects maintainability, correctness, or public API clarity.
Flag any accidental logging, telemetry, crash reporting, or network transfer of note contents,
vault paths, filenames with private context, API keys, APNs tokens, relay keys, or security-scoped bookmark data.

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Use Conventional Commits for commit messages
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Provide clear PR descriptions when submitting pull requests
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Syncs directly into Obsidian's iOS sandbox folder
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Do not store notes, index vaults, or proxy Markdown files through cloud backend
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Use Syncthing v2.x via Go/gomobile .xcframework as the sync engine
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:12:44.195Z
Learning: Implement vaultsync-notify as a Docker container for homeserver/NAS setups
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:13:01.122Z
Learning: Pattern matching in ignore rules should transparently handle multi-vault setups by allowing unanchored patterns (without leading `/`) to match at any depth; `.git` covers both root-level and vault-subdirectory instances
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:13:01.122Z
Learning: For users updating from a current build, keep the 3 silent default patterns (.Trash, .obsidian/workspace.json, .obsidian/workspace-mobile.json) untouched on disk and automatically show them as ON in derived state without migration sheet or disk changes
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:13:01.122Z
Learning: Do not bundle different plugin cache locations (e.g., Dataview's cache.db, Copilot's .copilot-index) under a single 'Plugin caches' preset; instead ship specific presets per plugin
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T10:13:01.122Z
Learning: Do not include `.gitignore` and `.gitattributes` in the git preset as these are tiny config files users typically want to sync; the git preset should only exclude the `.git/` directory
🔇 Additional comments (8)
ios/VaultSync/Services/SyncthingManager.swift (5)

79-89: LGTM — migration-safe defaults are now decoupled from the evolving preset catalog.

This hard-coded list addresses the earlier concern that future changes to IgnorePreset.recommended could silently mutate .stignore on existing vaults during startup. The first-run recommendation sheet can evolve independently.

Based on learnings: "For existing vaults during migration, keep the 3 silent default patterns … untouched on disk."


1490-1526: LGTM — safe read helper properly distinguishes "empty" from "unreadable".

readIgnorePatternsOrNil returning nil on decode failure allows all write flows to abort before overwriting a malformed .stignore. The display-only ignorePatterns(folderID:) wrapper returning [] is acceptable for UI contexts where a fallback is safe.


1528-1564: LGTM — write flows guard against unreadable state; read-only check is acceptably lenient.

togglePreset and addIgnorePattern both abort writes when current filters can't be parsed. isPresetActive using the lenient ignorePatterns() is fine since it's read-only — worst case, presets appear inactive on a decode failure, which is a safe fallback for display.


1573-1607: LGTM — deselect-aware merge preserves custom patterns; nonisolated static scanner is correct.

applyRecommendedFilters properly filters out managed entries before re-adding only enabled ones, so deselecting actually takes effect while user-added custom patterns survive. The nonisolated static modifier on scanFolderForKnownPatterns is appropriate since it doesn't touch actor state and is designed to be called from a detached task.


1609-1619: LGTM — UserDefaults-based first-run flag is straightforward.

The read-check-append in markRecommendationSheetShown has a theoretical TOCTOU window, but since these methods run on @MainActor and the worst outcome is a duplicate entry in the persisted array, this is benign.

ios/VaultSync/Views/SyncFilterRecommendationSheet.swift (3)

1-66: LGTM — sheet structure properly handles errors and marks state only on success.

The "Done" button now checks the return value from apply() and shows an alert on failure instead of silently dismissing. The "Skip" button correctly marks the sheet as shown without applying changes. The .task modifier for scanning is the right pattern for one-time async work tied to the view lifecycle.


72-120: LGTM — toggle bindings correctly route detected patterns to their presets.

detectedToggle properly checks if a detected pattern maps to a known preset and routes the toggle to enabledPresetIDs in that case, keeping the recommendation sheet and preset catalog in sync.


122-149: LGTM — scan logic is properly isolated and apply delegates to the manager.

The Task.detached call for the blocking bridge scanner keeps the main thread responsive during the filesystem walk. After await, state updates happen back on @MainActor as expected. Delegating apply() to SyncthingManager.applyRecommendedFilters keeps the sheet presentation-only and the read-modify-write safety logic centralized.


📝 Walkthrough

Walkthrough

Adds per‑vault Sync Filters: Go scanner reports heavy-folder detections; Swift models/presets and a bridge wrapper decode them; SyncthingManager APIs manage .stignore and recommendation state; new IgnorePatternsView and recommendation sheet UI; conflict/vault views integrate skip actions; tests, localizations, docs, and version bump included.

Changes

Sync Filters — Ignore Patterns

Layer / File(s) Summary
Specification & Data Models
docs/sync-filters-ux.md, ios/VaultSync/Models/DetectedPattern.swift, ios/VaultSync/Models/IgnorePreset.swift
Adds UX spec; DetectedPattern/DetectedScan decode scanner JSON; IgnorePreset defines built-in presets, recommended, and preset(forDetectedPattern:).
Folder Scanning (Go Backend)
go/bridge/folderscan.go
Implements ScanFolderForKnownPatterns(folderID) scanning vault root + one level (skip dot top-level dirs), aggregates file sizes/counts for candidate directories and returns stable JSON.
Scanner Tests (Go)
go/bridge/folderscan_test.go
Tests cover detection of .git and other candidates, empty/unknown folders, multiple candidates, nested aggregation, hidden-dir handling, and empty-directory behavior.
Bridge & Wrapper
ios/VaultSync/Services/SyncBridgeService.swift
Adds bridge wrapper SyncBridgeService.scanFolderForKnownPatterns(folderID) returning Go scanner JSON string for iOS to decode.
Service & Manager APIs
ios/VaultSync/Services/SyncthingManager.swift
Adds ignorePatterns, setIgnorePatterns, isPresetActive, togglePreset, addIgnorePattern, applyRecommendedFilters, scanFolderForKnownPatterns, and per-folder recommendation-sheet tracking via UserDefaults. Documents migration-safe defaultIgnorePatterns typing.
UI Screens: Patterns & Recommendations
ios/VaultSync/Views/IgnorePatternsView.swift, ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
IgnorePatternsView shows recommended presets, detected vault patterns (mapped to presets or shown as detected toggles), other presets, and a custom-pattern editor; toggles immediately update via SyncthingManager. SyncFilterRecommendationSheet runs a one-time scan, prechecks detections, and applies or skips selections, persisting shown state.
UI Integration: Toolbar & Navigation
ios/VaultSync/Views/ConflictDiffView.swift, ios/VaultSync/Views/ContentView.swift
ConflictDiffView adds "Always skip on this iPhone" overflow action that calls addIgnorePattern() and presents confirmation/error alerts. ContentView adds a "Sync Filters" NavigationLink in vault detail and conditionally presents the recommendation sheet when unseen.
Tests, Localization & Documentation
go/bridge/folderscan_test.go, ios/VaultSyncTests/*, ios/VaultSync/*.{lproj}, CHANGELOG.md, README.md, ios/project.yml
Adds Go + Swift tests, localization strings for English/German/zh-Hans, CHANGELOG/README v1.2.0 entry, and iOS project version bump.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

"A scanner roams through vaults wide,
Counting bytes the caches hide,
Toggle presets, skip with ease,
Recommendations aim to please,
Small rules keep sync serene and tried."

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows conventional-commit style with 'feat:' prefix and clearly describes the main change (sync filters UI implementation).
Linked Issues check ✅ Passed All core objectives from #1 are implemented: per-vault ignore patterns UI, preset groups (Workspace/Trash/Git/macOS/Copilot/Obsidian), folder scanning with sizes/counts, and Syncthing-compatible pattern semantics.
Out of Scope Changes check ✅ Passed All changes are scope-aligned: sync filter models/services, UI views, Go scanner bridge, localization, tests, and documentation directly support the ignore-patterns feature. Version bump and changelog reflect the feature addition appropriately.
Description check ✅ Passed PR objectives clearly articulate the feature scope, bridge implementation, model/service layer, UI components, localization coverage, and testing strategy with strong emphasis on data safety (guarded writes, read-modify-write atomicity).
No Private Note Leakage ✅ Passed Code does not leak note contents, vault paths, filenames, API keys, or tokens through logging, analytics, or network requests. Only hardcoded patterns and generic error messages are exposed.
Bounded Ios Background Work ✅ Passed Bounded work (folder root + 1 level, 6 patterns). Cancellation-aware via .task. Guards prevent duplicates. Returns aggregated data only. No private leaks or iOS constraint violations.
Bridge Contract Compatibility ✅ Passed ScanFolderForKnownPatterns uses gomobile-compatible types, returns valid JSON consistently, field names match between Go and Swift, and tests validate contract compatibility.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ignore-patterns

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

psimaker added 5 commits May 9, 2026 09:21
Detects heavy directories (.git, .copilot-index, node_modules, .obsidian/cache) in a vault and reports their byte size + file count.

Powers the "Found in this vault" section of the Sync Filters UI.
- IgnorePreset catalog with 6 presets (Workspace, Trash, Git, macOS, Copilot, Obsidian app cache); Recommended = Workspace + Trash
- DetectedPattern Swift mirror of the Go scanner JSON
- SyncthingManager: ignorePatterns / setIgnorePatterns / togglePreset / addIgnorePattern / scanFolderForKnownPatterns + per-folder recommendation-sheet flag in UserDefaults
- defaultIgnorePatterns now derived from IgnorePreset.recommended; legacy folders migrate silently (their existing 3 lines remain on disk and the derived state shows Workspace + Trash as active)
- 12 Swift unit tests covering catalog and sheet-flag persistence
- IgnorePatternsView: per-vault editor with Recommended, Found in this vault (scan results with size/file-count), Other presets, and Custom patterns sections; toggles write through immediately
- SyncFilterRecommendationSheet: first-run sheet shown on first vault-detail open per folder; pre-checks recommended + auto-checks any heavy folders the scan finds
- ContentView: NavigationLink in vaultDetailView between Conflicts and Shared With; .sheet(item:) trigger for the recommendation sheet
- ConflictDiffView: top-bar Menu with "Always skip on this iPhone" that adds the conflict's exact relPath to the folder's ignore list
All preset labels, descriptions, screen titles, sheet copy, and conflict-button strings translated. German and Chinese mirror the existing tone of the rest of the app.
In typical Obsidian use, the sync folder is the Obsidian root and individual vaults live as immediate subdirectories. The previous scanner only stat'd folder.Path/.git etc., so multi-vault layouts saw an empty "Found in this vault" section even when vaults contained large .git or .copilot-index folders.

Now: check the root and one level deep into non-hidden subdirs, aggregating matches per pattern (e.g. ".git in 3 vaults — 127 MB").

Pattern matching itself was already correct — Syncthing auto-expands unanchored patterns (lib/ignore/ignore.go:474-488), so .git, .Trash, and .obsidian/workspace.json already match at any depth without a **/ prefix. No preset or migration changes needed.

Tests: 2 new cases (nested vaults aggregate; hidden subdirs are not descended into to avoid double-counting). Full bridge suite passes.
@psimaker psimaker changed the title feat: sync filters UI (ignore patterns) — spec for review feat: sync filters UI for issue #1 May 9, 2026
@psimaker psimaker marked this pull request as ready for review May 9, 2026 09:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
go/bridge/folderscan.go (1)

121-124: ⚖️ Poor tradeoff

Silent error handling may hide permission issues.

The filepath.Walk error handler returns nil for all errors, silently continuing the traversal. While this prevents scan failures due to unreadable subdirectories, it could mask real issues like filesystem corruption or unexpected permission errors that might warrant user attention.

Given that this is a non-critical scan operation for UI hints, the current approach is acceptable. However, consider logging errors or tracking a count of skipped directories in a future iteration if users report incomplete scan results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go/bridge/folderscan.go` around lines 121 - 124, The filepath.Walk callback
currently swallows all errors by always returning nil, which can hide permission
or filesystem issues; update the anonymous Walk callback used with
filepath.Walk(root, ...) to surface or record errors instead of silently
ignoring them — for example, call a logger (e.g., log.Printf or the package
logger) with context including path and err, or increment a skippedDirs counter
when err != nil, then continue the traversal by returning nil so behavior stays
non-fatal; reference the anonymous callback used in the filepath.Walk call and
the root variable when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/sync-filters-ux.md`:
- Around line 3-7: Update the top-matter and any instances of pre-implementation
language to indicate the spec is implemented: replace the "Status: **draft for
review**" string with something like "Status: implemented" (or "implemented —
see PR/commit link"), update the "Last updated" date if needed, and remove or
reword sentences containing "before writing implementation code" and "before I
start building" (and the similar phrasing at lines 166-174) to reflect that the
design has been implemented and point readers to the implementation/PR where
applicable; search for and edit the exact phrases "draft for review", "before
writing implementation code", and "before I start building" to update them.

In `@ios/VaultSync/Models/IgnorePreset.swift`:
- Around line 60-62: The preset catalog deliberately omits node_modules but
localization strings for "Node modules" remain; either remove those unused
localization keys or add node_modules as a real preset. If you choose removal:
delete the "Node modules" entries from Localizable.strings (en, de, zh-Hans) and
any related NSLocalizedString keys. If you choose to expose it: add a new
IgnorePreset case (e.g., .nodeModules) and implement its display/localization,
include it in IgnorePreset.all, and update any pattern-detection mapping and the
test presetForDetectedPatternReturnsNilForUnknown to expect the new preset
instead of nil.

In `@ios/VaultSync/Services/SyncthingManager.swift`:
- Around line 79-85: defaultIgnorePatterns currently returns
IgnorePreset.recommended.flatMap(\.patterns), which ties silent migration
defaults to future catalog changes; change defaultIgnorePatterns to return a
hard-coded array containing only the three migration-safe silent patterns
[".Trash", ".obsidian/workspace.json", ".obsidian/workspace-mobile.json"] so
existing vaults are not implicitly mutated on startup, and keep any use of
IgnorePreset.recommended for new-folder UI/first-run recommendation flows
separate.

In `@ios/VaultSync/Views/SyncFilterRecommendationSheet.swift`:
- Around line 47-51: The Done button currently calls apply(), marks the sheet
shown and dismisses even if syncthingManager.setIgnorePatterns fails and apply()
only appends enabled patterns (never removes deselected ones). Change apply()
(or create applyChanges()) to return a Result/throwing error and to compute the
final .stignore contents by removing any previously-managed patterns that were
deselected and replacing them with the currently enabled set (use the same
identifiers the manager uses to recognize managed patterns). In the Button
action, await/catch the result of setIgnorePatterns (via the updated apply()),
and only call syncthingManager.markRecommendationSheetShown(folderID:) and
dismiss() on success; on failure surface/log the error to the user instead of
silently dropping it. Ensure you reference and update uses of setIgnorePatterns
and the apply() caller sites to handle the new error/return semantics.

---

Nitpick comments:
In `@go/bridge/folderscan.go`:
- Around line 121-124: The filepath.Walk callback currently swallows all errors
by always returning nil, which can hide permission or filesystem issues; update
the anonymous Walk callback used with filepath.Walk(root, ...) to surface or
record errors instead of silently ignoring them — for example, call a logger
(e.g., log.Printf or the package logger) with context including path and err, or
increment a skippedDirs counter when err != nil, then continue the traversal by
returning nil so behavior stays non-fatal; reference the anonymous callback used
in the filepath.Walk call and the root variable when making these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a84bdb8-fbd9-470a-9252-07a71c5786d6

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc3290 and 1c6a89a.

📒 Files selected for processing (16)
  • docs/sync-filters-ux.md
  • go/bridge/folderscan.go
  • go/bridge/folderscan_test.go
  • ios/VaultSync/Models/DetectedPattern.swift
  • ios/VaultSync/Models/IgnorePreset.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/ConflictDiffView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/IgnorePatternsView.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/VaultSyncTests/IgnorePresetTests.swift
  • ios/VaultSyncTests/SyncFiltersTests.swift
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*

⚙️ CodeRabbit configuration file

**/*: VaultSync syncs private Obsidian notes through Syncthing. Treat data loss,
privacy leaks, security regressions, and broken sync behavior as high priority.
Do not nitpick formatting unless it affects maintainability, correctness, or public API clarity.
Flag any accidental logging, telemetry, crash reporting, or network transfer of note contents,
vault paths, filenames with private context, API keys, APNs tokens, relay keys, or security-scoped bookmark data.

Files:

  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Models/DetectedPattern.swift
  • docs/sync-filters-ux.md
  • go/bridge/folderscan.go
  • ios/VaultSyncTests/SyncFiltersTests.swift
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/ConflictDiffView.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • ios/VaultSyncTests/IgnorePresetTests.swift
  • ios/VaultSync/Models/IgnorePreset.swift
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • go/bridge/folderscan_test.go
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/Views/IgnorePatternsView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Services/SyncthingManager.swift
ios/**/*.swift

⚙️ CodeRabbit configuration file

ios/**/*.swift: Focus on Swift 6 strict concurrency, Sendable/MainActor correctness, Task cancellation,
retain cycles, memory pressure, SwiftUI observation state, StoreKit/APNs flows, and iOS background execution limits.
Pay special attention to BGAppRefreshTask and BGContinuedProcessingTask behavior, expiration handling,
bounded work, and cleanup when the app is suspended or terminated.

Files:

  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Models/DetectedPattern.swift
  • ios/VaultSyncTests/SyncFiltersTests.swift
  • ios/VaultSync/Views/ConflictDiffView.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • ios/VaultSyncTests/IgnorePresetTests.swift
  • ios/VaultSync/Models/IgnorePreset.swift
  • ios/VaultSync/Views/IgnorePatternsView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Services/SyncthingManager.swift
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Review public documentation for technical accuracy, privacy/security claims, App Store-facing wording,
setup correctness, and consistency with the free app plus optional Cloud Relay subscription model.

Files:

  • docs/sync-filters-ux.md
go/bridge/**/*.go

⚙️ CodeRabbit configuration file

go/bridge/**/*.go: This code crosses the gomobile Swift-Go boundary. Verify exported signatures use only gomobile-safe primitive types,
preserve the JSON string contract, keep empty-string success conventions intact, and avoid breaking Swift decoding tests.
Review Syncthing lifecycle, locking, error strings, and noassets build assumptions carefully.

Files:

  • go/bridge/folderscan.go
  • go/bridge/folderscan_test.go
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: Do not expose raw Syncthing pattern syntax in the UI; instead use plain language descriptions like 'keep this off my iPhone' to help users understand file exclusion
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: The Sync Filters UI should be positioned on the vault detail screen between Conflicts and Shared With sections
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: All filter toggles should write through to `.stignore` immediately without requiring a save button
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: The vault scanner should check both the sync folder root and one level deep to detect heavy folders, and aggregate matches per pattern across multiple vaults
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: Preset catalog must include: workspace state (`.obsidian/workspace.json`, `.obsidian/workspace-mobile.json`) ON by default, trash (`.Trash`) ON by default, git repository (`.git`) OFF, macOS metadata (`.DS_Store`, `._*`) OFF, Copilot index (`.copilot-index`) OFF, and Obsidian app cache (`.obsidian/cache`) OFF
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: The first-run recommendation sheet should be shown only on the first vault detail screen open per vault, with state persisted via a UserDefaults array of folder IDs
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: In ConflictDiffView, add a toolbar menu option to 'Always skip on this iPhone' that adds the conflict's exact relative path (not a smart glob pattern) to the folder's ignore list
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: Use consistent terminology throughout the app: 'Sync Filters' for section title, 'Skip on this iPhone' and 'Always skip on this iPhone' for CTAs, avoiding 'Ignore patterns', 'Exclusions', and 'Filter rules'
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: Ship all new strings in English, German, and Simplified Chinese locales
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: Pattern matching should handle unanchored patterns (without leading slash) transparently, allowing patterns like `.git` to match at any depth without requiring `**/` prefix
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: For existing vaults during migration, keep the 3 silent default patterns (`.Trash`, `.obsidian/workspace.json`, `.obsidian/workspace-mobile.json`) on disk untouched and automatically show them as ON in the derived state without disk changes
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:37:05.022Z
Learning: For new vaults added after this feature lands, silently apply Recommended presets to prevent immediate sync conflicts, and show the first-run sheet with Recommended already checked and scan results pre-checked
🪛 LanguageTool
docs/sync-filters-ux.md

[uncategorized] ~80-~80: The operating system from Apple is written “macOS”.
Context: ...t| OFF (auto-on if scan finds it) | |macos| macOS metadata |.DS_Store, ._*` ...

(MAC_OS)


[style] ~143-~143: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... - No migration sheet. No disk changes. No surprise. For new vaults added after t...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~173-~173: The present participle “” normally requires “is”.
Context: ...allback. No strong opinion either way. Anything else missing? The whole point of doing this ...

(ANYONE_ELSE_WHO_IS_VBG)

🪛 markdownlint-cli2 (0.22.1)
docs/sync-filters-ux.md

[warning] 20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 33-33: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 91-91: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 121-121: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (10)
ios/VaultSync/zh-Hans.lproj/Localizable.strings (1)

462-496: LGTM! Clean localization implementation.

The Simplified Chinese strings correctly cover all Sync Filters UI elements, preset descriptions, and skip-flow messaging. The translations align with the feature requirements and maintain consistent terminology throughout. Based on learnings, the terminology correctly avoids exposing raw Syncthing pattern syntax and uses plain language descriptions.

go/bridge/folderscan.go (2)

30-38: LGTM! Clean preset catalog definition.

The heavyDirCandidates slice correctly defines the known patterns to detect, matching the preset catalog in the Swift layer. The pattern/label pairs align with the IgnorePreset definitions and the localization strings.


118-132: ⚡ Quick win

Remove the symlink concern—filepath.Walk does not follow symlinks.

Go's filepath.Walk explicitly does not follow symbolic links (it uses os.Lstat internally), so the vault cannot be traversed outside its boundaries through symlinks in .git, .obsidian, or subdirectories. The code is safe as written.

ios/VaultSync/Services/SyncBridgeService.swift (1)

179-183: LGTM! Correct bridge wrapper implementation.

The new scanFolderForKnownPatterns method correctly follows the established bridge service pattern, calling the Go function and returning the JSON string result. The signature matches the Go bridge contract and the comment clearly documents the return format.

ios/VaultSync/Models/DetectedPattern.swift (1)

1-16: LGTM! Clean model implementation.

The DetectedPattern and DetectedScan models correctly mirror the Go bridge JSON structure. The Sendable conformance is appropriate since all properties are value types, making these safe for concurrent use across actors. Using pattern as the id is correct since patterns are unique per the Go scanner's aggregation logic.

ios/VaultSync/Models/IgnorePreset.swift (1)

14-62: LGTM! Preset catalog correctly implements requirements.

The preset definitions align perfectly with the learnings:

  • workspace and trash are in recommended (auto-applied to new vaults)
  • git, macos, copilot, and obsidianCache are available but not recommended by default
  • Pattern strings match the Go scanner's heavyDirCandidates for proper detection mapping

Based on learnings, the preset catalog must include workspace state, trash, git repository, macOS metadata, Copilot index, and Obsidian cache with the specified default states.

ios/VaultSyncTests/IgnorePresetTests.swift (1)

1-47: LGTM! Comprehensive preset validation.

The test suite thoroughly validates the preset catalog against requirements:

  • Recommended presets match learnings (workspace + trash only)
  • All presets have non-empty patterns (prevents UI bugs)
  • Preset IDs are unique (prevents catalog conflicts)
  • Detection mapping works correctly for supported patterns

The test at line 43-46 confirms that node_modules intentionally has no preset mapping, which aligns with my earlier observation.

ios/VaultSync/en.lproj/Localizable.strings (1)

463-496: LGTM! Consistent terminology and complete coverage.

The English localization correctly implements the terminology guidelines from learnings:

  • Section title: "Sync Filters" (not "Ignore patterns")
  • Actions: "Skip", "Always skip on this iPhone" (user-friendly language)
  • Preset descriptions use plain language (e.g., "rarely useful on iPhone") rather than technical pattern syntax

All new UI strings, preset labels, and error messages are covered. Based on learnings, use consistent terminology throughout: 'Sync Filters' for section title, 'Skip on this iPhone' and 'Always skip on this iPhone' for CTAs.

ios/VaultSync/de.lproj/Localizable.strings (1)

462-495: LGTM! Complete German localization.

The German translations correctly cover all Sync Filters UI elements, maintaining consistent terminology:

  • "Sync-Filter" for the feature name
  • "Überspringen" / "Auf diesem iPhone immer überspringen" for skip actions
  • Clear, user-friendly descriptions for each preset

The translations align with the English source and follow the same structure as the other locale files. Based on learnings, ship all new strings in English, German, and Simplified Chinese locales.

go/bridge/folderscan_test.go (1)

71-73: ⚡ Quick win

Use structural JSON checks instead of raw string equality in empty-result tests.

Comparing to {"detected":[]} is brittle and can fail on non-semantic JSON formatting/order changes.

[ suggest_recommended_refactor ]

Refactor pattern
-    raw := ScanFolderForKnownPatterns(folderID)
-    if raw != `{"detected":[]}` {
-        t.Errorf("got %q, want empty detected list", raw)
-    }
+    raw := ScanFolderForKnownPatterns(folderID)
+    var result ScanResult
+    if err := json.Unmarshal([]byte(raw), &result); err != nil {
+        t.Fatalf("unmarshal failed: %v (raw=%s)", err, raw)
+    }
+    if len(result.Detected) != 0 {
+        t.Errorf("expected empty detected list, got %d (raw=%s)", len(result.Detected), raw)
+    }

Also applies to: 84-86, 256-258

Comment thread docs/sync-filters-ux.md Outdated
Comment on lines +60 to +62
static let all: [IgnorePreset] = [
.workspace, .trash, .git, .macos, .copilot, .obsidianCache,
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if nodeModules preset is defined elsewhere or if the design changed

# Search for any nodeModules preset definition
rg -n 'nodeModules.*IgnorePreset' ios/VaultSync/

# Check if the "Node modules" localization string is actually used in the UI
rg -n '"Node modules"' ios/VaultSync/Views/

# Look for related comments or documentation explaining the decision
rg -n -C2 'node_modules.*preset|preset.*node_modules' ios/VaultSync/

Repository: psimaker/vaultsync

Length of output: 44


🏁 Script executed:

# First, let's see what presets are actually defined in IgnorePreset.swift
cat -n ios/VaultSync/Models/IgnorePreset.swift | head -100

Repository: psimaker/vaultsync

Length of output: 2747


🏁 Script executed:

# Check if the test file exists and read around line 44
if [ -f ios/VaultSync/Models/IgnorePresetTests.swift ]; then
  cat -n ios/VaultSync/Models/IgnorePresetTests.swift | sed -n '35,55p'
fi

Repository: psimaker/vaultsync

Length of output: 44


🏁 Script executed:

# Find localization files and search for "Node modules"
find ios -name "*.strings" -o -name "*.stringsdict" -o -name "*Localizable*" | head -20

Repository: psimaker/vaultsync

Length of output: 622


🏁 Script executed:

# Search more broadly for node_modules mentions
rg -i 'node_modules' ios/VaultSync/ --type swift | head -20

Repository: psimaker/vaultsync

Length of output: 44


🏁 Script executed:

# Check if Go code mentions node_modules
rg -i 'node_modules' . --type go | head -20

Repository: psimaker/vaultsync

Length of output: 334


🏁 Script executed:

# Find the test file
find ios -name "*IgnorePresetTests*" -o -name "*IgnorePresetTest*"

Repository: psimaker/vaultsync

Length of output: 105


🏁 Script executed:

# Search for the test file in different locations
find ios -type f -name "*Test*.swift" | rg -i preset

Repository: psimaker/vaultsync

Length of output: 105


🏁 Script executed:

# Look for any Swift test files
find ios -type f -name "*Tests.swift" | head -20

Repository: psimaker/vaultsync

Length of output: 441


🏁 Script executed:

# Search the localization strings for "Node modules"
rg -n "Node modules" ios/VaultSync/

Repository: psimaker/vaultsync

Length of output: 301


🏁 Script executed:

# Check the entire content of the English localization file to see if "Node modules" is present
cat ios/VaultSync/en.lproj/Localizable.strings | grep -i "node"

Repository: psimaker/vaultsync

Length of output: 95


🏁 Script executed:

# Read the test file to verify the claim about line 44
cat -n ios/VaultSyncTests/IgnorePresetTests.swift

Repository: psimaker/vaultsync

Length of output: 1940


node_modules is intentionally excluded from presets despite Go detection.

The Go scanner detects node_modules and the codebase includes localization strings for "Node modules" (en, de, zh-Hans). However, the Swift preset catalog deliberately omits it—confirmed by the test presetForDetectedPatternReturnsNilForUnknown at line 44 of IgnorePresetTests.swift, which explicitly expects nil when checking node_modules.

This means users will see node_modules flagged when it's detected in the vault, but cannot toggle it as a preset and must add it manually as a custom pattern instead.

If node_modules will never be exposed as a preset, remove the unused localization strings to avoid confusion. If it should become a preset in the future, add it to the catalog.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/VaultSync/Models/IgnorePreset.swift` around lines 60 - 62, The preset
catalog deliberately omits node_modules but localization strings for "Node
modules" remain; either remove those unused localization keys or add
node_modules as a real preset. If you choose removal: delete the "Node modules"
entries from Localizable.strings (en, de, zh-Hans) and any related
NSLocalizedString keys. If you choose to expose it: add a new IgnorePreset case
(e.g., .nodeModules) and implement its display/localization, include it in
IgnorePreset.all, and update any pattern-detection mapping and the test
presetForDetectedPatternReturnsNilForUnknown to expect the new preset instead of
nil.

Comment thread ios/VaultSync/Services/SyncthingManager.swift Outdated
Comment thread ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
psimaker added 2 commits May 9, 2026 11:47
- Version 1.1.0 → 1.2.0 (minor bump for the new Sync Filters feature)
- Build 20 → 21 (App + Widget targets)
- CHANGELOG: new [1.2.0] entry covering Sync Filters, vault scan, conflict-skip, first-run sheet, custom patterns, localization
- README: What's New block replaced with v1.2.0 highlights
Three follow-ups from the automated review:

1. SyncthingManager.defaultIgnorePatterns is now hard-coded to the historical migration-safe set (.Trash + the two workspace.json variants) instead of being derived from IgnorePreset.recommended. Future changes to the catalog can no longer silently mutate .stignore on existing vaults during the startup auto-merge. The recommendation sheet uses IgnorePreset.recommended directly.

2. SyncFilterRecommendationSheet.apply() now properly handles deselect and surfaces errors. It computes the final pattern list by removing all sheet-managed entries (presets + detected items) from the existing .stignore first, then re-adds only the currently enabled ones. Custom patterns the user added previously are preserved. Done returns the SyncUserError from setIgnorePatterns; on failure an alert is shown and the sheet is neither marked-shown nor dismissed, so the user can retry.

3. Spec doc status updated from "draft for review" to "implemented", and the open-questions section reframed as "future considerations" for follow-up reference.

CodeRabbit's nitpick about node_modules localization being unused is incorrect (the strings are surfaced via the Go scanner's item.label when node_modules is detected), so it stays as-is.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/sync-filters-ux.md`:
- Around line 20-27: The markdown fenced code blocks shown (the block starting
with "Vault                    (name, path)" and the blocks showing the Sync
Filters UI like "┌─ Sync Filters — \"My Vault\" ─────────────┐", "┌─ Sync
Filters ──────────────────────────┐", and the menu block starting with "⋯ menu")
are missing language identifiers which triggers MD040; update each opening fence
to use a language tag (use "text") so they become ```text ... ``` ensuring all
four affected fenced blocks include the language identifier.

In `@ios/VaultSync/Services/SyncthingManager.swift`:
- Around line 1491-1497: The ignorePatterns(folderID:) helper currently swallows
JSON decode failures by returning an empty list; change it to surface read/parse
errors instead (e.g., make ignorePatterns throw or return Result<[String],
Error> or an optional) so callers can abort write flows when the current
.stignore cannot be parsed; update callers (e.g., any code invoking
ignorePatterns and subsequent read-modify-write operations) to check the
error/optional and refuse to perform writes when
SyncBridgeService.getFolderIgnores(folderID:) output cannot be decoded, ensuring
existing ignore rules aren’t silently overwritten.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 69d787a4-6e71-4ae9-945c-6b81829571d0

📥 Commits

Reviewing files that changed from the base of the PR and between 22b7416 and ad24643.

📒 Files selected for processing (6)
  • docs/sync-filters-ux.md
  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
✅ Files skipped from review due to trivial changes (3)
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test
🧰 Additional context used
📓 Path-based instructions (3)
ios/**/*.swift

📄 CodeRabbit inference engine (README.md)

Follow Swift API Design Guidelines

Use Swift strict concurrency where applicable

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift

⚙️ CodeRabbit configuration file

ios/**/*.swift: Focus on Swift 6 strict concurrency, Sendable/MainActor correctness, Task cancellation,
retain cycles, memory pressure, SwiftUI observation state, StoreKit/APNs flows, and iOS background execution limits.
Pay special attention to BGAppRefreshTask and BGContinuedProcessingTask behavior, expiration handling,
bounded work, and cleanup when the app is suspended or terminated.

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
**/*

⚙️ CodeRabbit configuration file

**/*: VaultSync syncs private Obsidian notes through Syncthing. Treat data loss,
privacy leaks, security regressions, and broken sync behavior as high priority.
Do not nitpick formatting unless it affects maintainability, correctness, or public API clarity.
Flag any accidental logging, telemetry, crash reporting, or network transfer of note contents,
vault paths, filenames with private context, API keys, APNs tokens, relay keys, or security-scoped bookmark data.

Files:

  • ios/VaultSync/Services/SyncthingManager.swift
  • ios/VaultSync/Views/SyncFilterRecommendationSheet.swift
  • docs/sync-filters-ux.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Review public documentation for technical accuracy, privacy/security claims, App Store-facing wording,
setup correctness, and consistency with the free app plus optional Cloud Relay subscription model.

Files:

  • docs/sync-filters-ux.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:00.268Z
Learning: Use Conventional Commits for commit messages
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:00.268Z
Learning: Provide clear PR (pull request) descriptions
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:15.932Z
Learning: For users updating from a previous build, keep existing silent default patterns (`.Trash`, `.obsidian/workspace.json`, `.obsidian/workspace-mobile.json`) on disk untouched without migration sheet or surprise disk changes
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:15.932Z
Learning: Do not include 'Plugin caches' as a single umbrella preset; instead use specific narrow presets per plugin since different plugins store caches in different locations
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:15.932Z
Learning: Do not include general `.gitignore` or `.gitattributes` in presets, as these are typically small text files that users want to sync for configuration purposes
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:15.932Z
Learning: The Sync Filters feature should be placed per-vault on the vault detail screen as a link between the Conflicts and Shared With sections
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-05-09T09:53:15.932Z
Learning: Pattern matching should handle multi-vault setups transparently by automatically expanding unanchored patterns (without leading `/`) to match at any depth, so `.git` covers both root-level and nested vault `.git` directories
🪛 LanguageTool
docs/sync-filters-ux.md

[uncategorized] ~80-~80: The operating system from Apple is written “macOS”.
Context: ...t| OFF (auto-on if scan finds it) | |macos| macOS metadata |.DS_Store, ._*` ...

(MAC_OS)


[style] ~143-~143: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... - No migration sheet. No disk changes. No surprise. For new vaults added after t...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.22.1)
docs/sync-filters-ux.md

[warning] 20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 33-33: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 91-91: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 121-121: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (2)
ios/VaultSync/Services/SyncthingManager.swift (1)

79-89: Migration-safe default ignore patterns are correctly decoupled from evolving presets.

Nice fix: the silent auto-apply set is now explicitly pinned, so future preset catalog changes won’t silently change existing vault .stignore files.

ios/VaultSync/Views/SyncFilterRecommendationSheet.swift (1)

48-55: Apply flow now correctly handles deselection, preserves custom patterns, and surfaces save failures.

This addresses the risky path well: success-only dismissal plus managed-pattern replacement is the right behavior for reliable per-vault filter updates.

Also applies to: 60-64, 145-160

Comment thread docs/sync-filters-ux.md
Comment on lines +20 to +27
```
Vault (name, path)
Sync Status (state, completion, errors)
Conflicts (when present)
► Sync Filters ← new
Shared With (devices)
Rescan Vault
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks (MD040).

At Line 20, Line 33, Line 91, and Line 121, the fenced blocks are missing a language tag, which can keep markdownlint warnings active in CI.

🛠️ Minimal lint-fix diff
-```
+```text
 Vault                    (name, path)
 ...
-```
+```

-```
+```text
 ┌─ Sync Filters — "My Vault" ─────────────┐
 ...
-```
+```

-```
+```text
 ┌─ Sync Filters ──────────────────────────┐
 ...
-```
+```

-```
+```text
 ⋯ menu
 └─ Always skip on this iPhone
-```
+```

Also applies to: 33-61, 91-107, 121-124

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/sync-filters-ux.md` around lines 20 - 27, The markdown fenced code
blocks shown (the block starting with "Vault                    (name, path)"
and the blocks showing the Sync Filters UI like "┌─ Sync Filters — \"My Vault\"
─────────────┐", "┌─ Sync Filters ──────────────────────────┐", and the menu
block starting with "⋯ menu") are missing language identifiers which triggers
MD040; update each opening fence to use a language tag (use "text") so they
become ```text ... ``` ensuring all four affected fenced blocks include the
language identifier.

Comment thread ios/VaultSync/Services/SyncthingManager.swift Outdated
Read-modify-write flows in SyncthingManager (togglePreset, addIgnorePattern, and the new applyRecommendedFilters used by SyncFilterRecommendationSheet) now abort with a SyncUserError when the bridge response cannot be decoded, instead of treating decode failure as an empty list. A malformed read could otherwise have silently overwritten the user's existing .stignore on the next write — exactly the data-loss class CodeRabbit flagged.

- New private readIgnorePatternsOrNil() helper distinguishes empty list from decode failure.
- Public ignorePatterns() keeps returning [String] for display (with \`?? []\`), so view code that just renders a list isn't forced to handle the nil case.
- New applyRecommendedFilters(...) consolidates the recommendation sheet's "preserve custom patterns + deselect-aware merge + safe read" logic next to the rest of the filter API.
- L10n: en/de/zh-Hans copy for the new error message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ignore files/folders by mask (like Syncthing)

1 participant