Skip to content

A config exclude that covers a scan root silently empties it #25

Description

@alies-dev

What happens

Config exclude globs are matched against walked paths without exempting the scan roots themselves, so an exclude that covers a root silently empties it:

$ cat todo-by.toml
exclude = ["docs/**"]
$ todo-by --files docs
$ echo $?
0

Nothing is listed and the exit code is 0, indistinguishable from a clean scan of an empty directory.

The contract is ambiguous

The README makes two statements that pull in opposite directions here:

  • "A path named on the command line is where the walk starts rather than something it found, so .gitignore does not apply to it... naming an ignored directory scans it in full." Root semantics: explicit intent beats ignore rules.
  • "The exclude config key covers whatever .gitignore does not." Readable as: excludes are deliberately stronger than .gitignore, a veto that root semantics do not pierce.

.gitignore honors the first statement (a gitignored directory named as a root is walked in full). Excludes follow neither statement cleanly: they do veto roots, but silently, with exit 0 and no notice, while the same "nothing usable was named" situation for version control metadata prints a stderr notice and a missing path exits 2.

Options

  1. Root semantics win: excludes prune the walk below a root but never veto a named root. Consistent with .gitignore handling; todo-by docs always scans docs.
  2. Excludes stay a hard veto, but loudly: a root eliminated by an exclude gets a stderr notice, and a scan where every root was excluded stops looking like a clean run (same principle as Fix: a scan that could not run could still exit 0 #23).

Either option also needs a sentence in the README saying which rule excludes follow.

Why this matters for #24

Excludes matching walked spellings means two spellings of one directory can differ in coverage. #24 collapses exact-duplicate roots by canonical path, keeping the first spelling; when the kept spelling is the excluded one, the coverage the dropped spelling had is lost:

$ ln -s excluded alias    # exclude = ["excluded/**"]
$ todo-by excluded alias

Under option 1 both spellings cover the same set again and the collapse in #24 is sound as shipped. Under option 2 the notice makes the loss visible. Special-casing aliases inside the collapse itself was prototyped during the #24 review and rejected: it reintroduces duplicate output for a plain absolute/relative root pair, and leaves the single-root case above unfixed either way.

Not a regression

Pre-existing on a single root with no overlap involved. Surfaced during the #24 review rather than introduced by it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions