Ignore rules & paths
Documentation/Native client workflows
Ignore rules & paths5 min read

Ignore rules and path semantics

On this page

Scribe applies the same repository path rules on every platform. Ignore rules control discovery of untracked files; they never hide tracked changes.

Canonical paths

A path is UTF-8, /-separated, relative, with no empty, ., or .. components, at most 8192 bytes, components at most 255 bytes. Rejected on every platform at add and submit time: backslashes, control characters, < > : " | ? *, reserved device names (CON, PRN, AUX, NUL, COM1-COM9, LPT1-LPT9, COM¹/COM²/COM³, LPT¹/LPT²/LPT³, with or without extension), components ending in a dot or space, and any .scribe component immediately followed by _state (both compared under the same fold as collisions). A change set additionally refuses a file named .scribe, since every working copy holds that name as a directory. Symbolic links, junctions, and reparse points are never followed, added, or synced through; they are reported.

.scribe/ is Scribe's namespace inside the repository, not a reserved directory. Only .scribe/_state/ is local, machine-specific workspace state, hard-reserved by Scribe itself rather than by an ignore rule: scribe add .scribe/_state/... is refused as reserved local workspace state, and sync or checkout may change anything else under .scribe/ but never deletes or overwrites .scribe/_state/. Everything else under .scribe/ — at the root or anywhere deeper, such as x/.scribe/flows/build.yml — is ordinary versioned repository content. Secrets are never written anywhere under .scribe/, at any level of the namespace.

Case and normalization collisions: the server keeps a shared snapshot index of NFC-normalized paths using pinned Unicode 17.0.0 full case folding after NFD, plus the NTFS dotless-i equivalence. ß, ẞ, and ss fold together, as do ſ and s or ς and σ. Each branch pins its own root, and the server rejects a submission that would create two paths mapping to the same key (path_collision), including within one submission. Windows/NTFS and macOS/APFS would otherwise map them to one file. Matching of ignore rules is case-sensitive on every platform; lock rules, by contrast, match under this collision fold (see lock rules).

Long paths: Windows paths over 240 characters are opened with the \\?\ extended-length prefix. A 4,000-character path is tested on Linux and Windows (platform_behaviour.rs); macOS caps most syscalls at 1,024 bytes and is not tested for long paths.

Executable bits are stored in file records; applied on Unix, stored only on Windows.

Ignore files

  • Syntax: Git-style via the ignore crate's matcher, with comments, blank lines, \# and \! escapes, *, ?, character classes, **, trailing-slash directory patterns, leading-slash anchoring to the rule file's directory, negation, UTF-8, spaces, LF or CRLF, trailing-space escaping. Unclosed character classes are literal (as in Git). A malformed rule (for example an invalid range) is reported rather than treating the file as empty. Status/scan retain available findings and exit 1 when discovery is incomplete; do not interpret that partial report as a clean workspace.
  • Sources and precedence, lowest to highest: --global-ignore <file>; workspace-local .scribe/_state/ignore; versioned .scribeignore files from the root to the deepest applicable directory. Within one file the last matching rule wins. Deeper versioned files override shallower ones.
  • Excluded parents: an ignored directory is not traversed, so a negation in a deeper file cannot recover its contents. scribe ignore explain reports via_parent in that case.
  • Ignore rules affect only untracked discovery and recursive add. Tracked files remain eligible for scanning; scribe status --scan reports their modifications and deletions regardless of rules. Rules never delete data and are not access control. Versioned ignore files are ordinary tracked files.
  • Scribe does not read .gitignore or global Git configuration, and does not apply implicit hidden-file rules or game-engine ignore templates.
  • scribe add --force <path> overrides ignore matching only; it never overrides containment, the .scribe/_state/ reservation, links, or name validity.
  • scribe ignore explain <path> prints ignored/not ignored, the deciding file, line, and pattern, and whether a parent directory decided.

.scribeignore and .scribe/config.toml have different authority. Ignore files govern local untracked discovery. Only the accepted locks section of .scribe/config.toml on main governs mandatory locks across all branches; nested or locally edited lock settings do not override it. Profiles in that same config are branch-scoped; they do not change ignore behavior. See mandatory lock rules.

Fingerprints

The baseline records size and modification time as reported by the filesystem, together with the moment that observation was taken. Equal fingerprints skip hashing in status only when the recorded modification time is older than the observation by the filesystem's timestamp granularity, so a same-length save inside that window cannot pass as unchanged; status --verify always hashes. Fingerprints are caches, not proof of unchanged bytes. Resolution differs by filesystem (NTFS 100 ns ticks; some filesystems 1 or 2 s), so only exact equality is meaningful.

Default status inspects opened and workflow paths only; --scan adds unopened tracked changes and untracked discovery, and --verify hashes contents. A directory at an expected file path, or a file blocking an ancestor directory, is not_file. Unsafe links and inspection failures are reported as per-path problems, not mistaken for ordinary missing files. See status and scanning.

Concurrent external writers

Submit re-stats a file after hashing it and re-hashes every chunk range while uploading, so a file that changes during a submit is refused. A before/after stat is not an atomic snapshot: a writer that modifies a file between the final stat and the upload read is caught by the chunk digest check; a writer that modifies and restores bytes within one fingerprint tick is not. Editors should be quiescent during submit.

Source docs/ignore-and-paths.mdSnapshot 2b8dcccb