FeanorFS mirrors the current contents of a workspace folder. This document records why we choose what to sync, what to skip by default, and what we deliberately do not do.
- Sync the working directory, not the git index. Untracked and gitignored paths are often the highest-value files (
.env, local config, scratch notes, WIP). FeanorFS exists to move that context between machines. - No git coupling. A workspace does not need to be a git repo. Honoring
.gitignorewould pull in nested rules, negation, global excludes, and submodule semantics — complexity that belongs in git, not in a sync tool. - Ongoing cost matters more than first sync. Build and package-manager trees are not just large once; they churn on every build (
target/,node_modules/, …). The watcher would rehash, encrypt, and upload gigabytes per compile. A "sync everything, first sync is slow" policy makes the tool unusable on typical Rust/Node projects, not merely slow on day one. - Small frozen defaults, not a growing framework list. Expanding
DEFAULT_IGNORESto dozens of framework-specific directories is a maintenance treadmill (the slippery slope). Defaults stay short; admission to the list is gated by a written criterion below. - One optional escape hatch without project litter.
feanorfs ignore <pattern>stores project-specific exclusions in private global state. Most users should never need it.
- All files under the workspace root, including hidden files and paths that
.gitignorewould exclude (e.g..env,.vscode/, local overrides). - Non-git workspaces (
feanorfs start ~/notes) behave the same.
A representative multi-machine flow mirrors .env and tracked edits together:
| Machine | After FeanorFS sync |
|---|---|
| Linux (development) | .env with local keys; src/app.rs edited but uncommitted |
| macOS (build/test) | the same .env and src/app.rs; Git reports src/app.rs as modified |
Both machines show the tracked edit as uncommitted relative to their own local
Git baseline. That dirty state is the shared work-in-progress — the working
tree is local Git baseline + shared FeanorFS WIP — not a synchronization
error. FeanorFS mirrors file contents and lets each clone's Git report them
truthfully; it never stages, commits, resets, or touches .git/.jj.
.env syncs because it is a normal file on disk (gitignored or not).
Machine-generated build output is handled by DEFAULT_IGNORES or explicit
feanorfs ignore rules, never by honoring .gitignore. See
usage.md for the guarded Git publication flow.
| Path | Reason |
|---|---|
.git/ |
VCS metadata — not user work product |
.jj/ |
Jujutsu VCS metadata — never transport repositories or working-copy state |
Legacy .feanorfs/ and .feanorfsignore |
Imported once into global state, removed from the project, and never transported |
.feanorfs-tmp-* |
Crash-safe sibling write staging; stale files are cleaned automatically |
| Symlinks | Reported by status as sorted, deduplicated skipped paths; links are never followed |
Valid CACHEDIR.TAG directories |
Regeneratable cache trees declared by their owning tools; contents remain untouched on disk |
Built-in patterns in client/src/local.rs — applied by the scanner, watcher, and prune-ignored. Current list:
target/ node_modules/ .DS_Store *.swp *~ .venv/ __pycache__/ dist/ build/ .next/ .cache/
These cover the highest-churn, highest-volume artifact trees across common stacks without per-project configuration.
A path may be added to DEFAULT_IGNORES only if all of the following hold:
- Regeneratable — reproducible from source + lockfiles or a single install/build command.
- Typically large or high-churn — would dominate upload bandwidth, CPU, or watcher debounce if synced.
- Not user-authored content — unlikely to hold scratch work, secrets, or config the user wants on another machine.
If a candidate fails (3), it stays out of defaults even when gitignored in most repos (e.g. tmp/, log/, bare vendor/).
| Alternative | Why not |
|---|---|
| Sync everything | Ongoing artifact churn; unusable on real projects |
Honor .gitignore |
Excludes .env and WIP; git semantics; not all workspaces are repos |
| Large framework denylist (~50+ entries) | Slippery slope; every new bundler/framework adds maintenance |
--use-gitignore as default |
Same exclusion problem; extra mode surface |
Use feanorfs ignore <pattern> for a project-specific artifact directory not in DEFAULT_IGNORES (for example, custom out/). Run feanorfs ignore to list rules, feanorfs ignore --remove <pattern> to remove one, or feanorfs ignore --clear to clear them. Rules use gitignore syntax and live under the workspace's opaque directory in ~/.feanorfs; no file is added to the project.
Ignoring never deletes. A path that was already tracked when a rule is added is frozen, not tombstoned: the scanner keeps reporting its cached identity, so the remote copy survives, nothing is re-downloaded, and local edits stay invisible to sync until the rule is removed. Only files that are genuinely absent from disk (and not covered by any rule) become deletions.
- Not required for typical Rust/Node/Python projects — defaults cover the heavy dirs.
- Not a substitute for git — duplicating an entire
.gitignoredefeats the product goal (sync what git ignores). - Pruning removes excluded paths from encrypted mirror state without deleting the local files. Hard-excluded VCS and legacy metadata are cleaned from transport/conflict state automatically.
Cachedir Tag Specification: tools mark regeneratable cache directories with a CACHEDIR.TAG file (Cargo writes one in target/). Skipping tagged directories is principled and self-maintaining — tool authors declare caches; FeanorFS does not curate framework names.
FeanorFS prunes a tagged directory from both main-workspace and agent-workspace scans only when CACHEDIR.TAG starts with this exact signature, including the trailing LF:
Signature: 8a477f597d28d172789f06886806bc55
A missing LF, different signature, non-file tag, or symlinked tag is invalid and does not prune the directory. A tag at the workspace root is deliberately exempt: pruning the root could hide the whole workspace and turn existing tracked files into remote deletions, so the root tag and its siblings sync normally. FeanorFS never follows links to inspect tags and never deletes skipped cache contents. Tag support complements (not replaces) the small DEFAULT_IGNORES list — e.g. node_modules/ does not use the tag today.
| Concern | Location |
|---|---|
DEFAULT_IGNORES, hard exclusions, global rules |
agent-core/src/local/walker.rs and workspace_layout.rs |
| Prune tracked paths matching ignores | client/src/commands.rs — prune_ignored |
| Agent workspace scan | Separate global agents/<name>/worktree; same ignore machinery |
Git ignore machinery is explicitly disabled on the walker (git_ignore(false), git_exclude(false), git_global(false)).