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, with or without extension), components ending in a dot or
space, and anything under .scribe/. Symbolic links, junctions, and reparse
points are never followed, added, or synced through; they are reported.
Case and normalization collisions: the server keeps a current_fold index of
NFC-normalized, Unicode-lowercased paths per branch and 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.
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
ignorecrate'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, and commands that discover files refuse to proceed rather than treating the file as empty. - Sources and precedence, lowest to highest:
--global-ignore <file>; workspace-local.scribe/ignore; versioned.scribeignorefiles 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 explainreportsvia_parentin that case. - Ignore rules affect only untracked discovery and recursive
add. Tracked files are always scanned;scribe status --scanreports modifications and deletions of tracked files regardless of rules. Rules never delete data and are not access control. Versioned ignore files are ordinary tracked files. - No
.gitignore, no global Git configuration, no hidden-file policy, no engine templates are read. scribe add --force <path>overrides ignore matching only; it never overrides containment,.scribe/, links, or name validity.scribe ignore explain <path>prints ignored/not ignored, the deciding file, line, and pattern, and whether a parent directory decided.
Fingerprints
The baseline records size and modification time as reported by the
filesystem. Equal fingerprints skip hashing in status; status --verify
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.
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.