Skip to content
Ferralk

Ferralk / Architecture

ADR-0011: POSIX-conservative walker defaults

  • Status: Accepted (amended 2026-09-24)
  • Date: 2026-08-18

Context

ferralk is a library, not a tool like ripgrep: defaults that silently filter or transform results ("why are files missing?") surprise library consumers. Everything behavior-changing should be an explicit builder call.

Decision

OptionDefault
follow_symlinksoff
respect_git_ignoreoff
* matches leading-period namesno (match_hidden opts in)
sortingoff (unsorted, nondeterministic order)
ErrorPolicyCollect
threadsmin(available_parallelism(), metadata concurrency ceiling, 256); explicit budgets clamp to 1..=256

The metadata concurrency ceiling is unbounded except on Apple Silicon macOS, where it is hw.perflevel0.cpusperl2 — the performance cores sharing one L2. A warm walk is not CPU work: 95% of its samples sit in openat, getdirentries64 and close, and on that platform the kernel's namespace layer serves those fastest when the workers stay inside one performance cluster. Past it, aggregate throughput falls rather than plateaus. docs/benchmark-evidence.md carries the measurement, including the raw C and zlob controls that show the knee is the platform's rather than ferralk's.

Consequences

  • Least surprise, POSIX-glob semantics by default; deterministic order and ignore handling cost only those who ask for them.
  • Consumers like Palamedes configure their policy once in the builder (gitignore on, symlinks off, collect errors).
  • The thread default is a starting point sized for the walk shape a glob walker usually gets, not a reading of the core count. A walk whose per-entry matching cost dwarfs its filesystem cost — measured, that means a serial walk roughly four times its metadata-only time — is faster with more workers and should call Walker::threads.

Amendment, 2026-09-24: excludes cover a leading period

The * row governs what a walk selects: include patterns. Walker excludes cover a leading period whatever match_hidden says, so they apply inside every directory the walk enters, hidden ones included, the way a .gitignore line does (#424). An exclude that stopped at a hidden directory left entries in the result that every ignore-style tool removes, which is the kind of surprise this ADR exists to avoid. What a walk can select is unchanged.

Amendment only; the decision above is unchanged.