# Current boundaries

- Commands are argv arrays run with `shell=false`. The executor is a local runner, not a security sandbox: supplied commands have the current user's rights, and only the direct child process is signalled on timeout or cancellation, not its grandchildren. A timed-out task is a failure even if its SIGTERM handler exits 0 within `grace_ms`.
- Cache identity is a SHA-256 Merkle fingerprint covering the command, declared input digests, declared outputs, the explicit `environment` map (as value digests), the dependencies' fingerprint digests and, when `hash_executables` is on, the digest of each task's program resolved as an explicit path or on `PATH` (POSIX lookup; no `PATHEXT`). Undeclared files, libraries and interpreters loaded by a program, and the inherited process environment are not part of the fingerprint, so builds are reproducible only as far as they are declared.
- Tasks without declared outputs always execute, and every task that depends on them waits for them on every run.
- With the default `propagate: "fingerprint"`, a producer whose fingerprint changed invalidates its dependents even if the produced bytes are identical (a producer rebuilt from unchanged inputs keeps its fingerprint, so lost or tampered artifacts do not cascade). With `propagate: "content"`, dependencies only order execution and invalidation follows declared file digests, so a dependent that does not declare the files it consumes is not invalidated by its producer at all.
- Success requires an exit code of 0 and every declared output present as a regular file; outputs are not validated beyond existence and digest.
- Journals record what happened and the cache is persisted after every wave, so a cancelled or interrupted run keeps the waves that finished; the wave in flight is re-executed rather than resumed, so side effects should be idempotent. The journal keeps a 64 KiB tail of each attempt's output and `.taskforge/logs/` keeps the complete output (unless `options.logs` is false); both hold whatever the command printed, while environment values appear only as digests. Library callers that pass raw values to `run` get raw values back in fingerprints.
- Declared paths are relative, without `..`, drive letters or backslashes; symbolic links inside the workspace are refused. Paths may contain spaces.
- Output ownership is checked case-insensitively, which is stricter than necessary on case-sensitive file systems.
- Concurrency bounds the ready wave (1–256); it does not limit what a single command spawns. Concurrent hostile workspace mutation is not isolated.
- A single CLI request is limited to 16 MiB of UTF-8 input; timeouts range from 1 ms to 24 hours, grace periods from 0 to 60 s, and retries from 0 to 10.
- There is no distributed scheduling, no container or cluster backend and no remote cache.
- Fingerprint version 2 (Merkle nodes) does not match version 1 cache entries, so upgrading rebuilds once. Digest comparisons are exact; a SHA-256 collision is not considered.
