Found by a blind dogfood run against v0.1.90-21-gf56aa72.
Symptom
A path that does not exist is reported as "a project root without a manifest", and exits 1 where the published contract says 2.
$ civitai app validate /nope/does/not/exist ; echo $?
✗ 1 validation error(s) in /nope/does/not/exist:
- block.manifest.json not found at project root /nope/does/not/exist
Error: validation failed
1
Two problems in one line:
- Wrong diagnosis. "not found at project root X" states that X is a project root and merely lacks a manifest. The truth is that X is not there at all. A first-time author reads this and starts hunting for a typo in the filename rather than in the path.
- Wrong exit code. The exit-code contract published from
exitCodeDocs draws the split explicitly for a named local path that is not there: it exits 2 (a mistake about the invocation), not 1.
A file that is not a directory is worse — a raw syscall error with a concatenated nonsense path:
$ civitai app validate ./README.md ; echo $?
Error: stat README.md/block.manifest.json: not a directory
1
This is the third copy of an inconsistency already fixed twice
The identical mistake, two commands, after #251:
$ civitai generate --input /nope/x.json --print-input
Error: --input /nope/x.json: no such file — pass a path that exists, or `--input -` to read
the graph from stdin; produce a starting point with `civitai generate "a cat" --print-input`
rc=2
$ civitai app validate /nope
- block.manifest.json not found at project root /nope
rc=1
Same class of error, same user mistake. One names the fix and exits 2; the other is bare and exits 1. #242, #245 and #251 each closed one copy of "a path failure is sorted by the wrong axis"; this is the remaining one, and it sits on the command a new author runs most.
AGENTS.md item 24 is the governing context — the split is by the author's mistake, not by the errno.
Suggested fix
In app validate's path resolution, os.Stat the target before looking for the manifest and branch three ways:
- does not exist →
asUsageError (exit 2), message naming the next command (civitai app create, or civitai app validate <dir> if they meant a different directory);
- exists but is not a directory →
asUsageError (exit 2), saying so plainly rather than surfacing stat X/block.manifest.json: not a directory;
- is a directory with no manifest → the current message and behaviour, which are correct.
Keep the "directory with no manifest" case as-is: it is a genuine validation finding, not a usage error.
Test coverage this needs
Pin the exit code with errors.Is(err, ErrUsage), never message text — per AGENTS.md item 7, the sentinels carry no visible text, so a message assertion says nothing about the code. Measured on #251: an equivalent one-token mutation passed a green suite because every existing assertion was on wording.
Rows: missing path → 2; a regular file → 2; a real project dir with no manifest → 1 (control, unchanged); a valid project → 0 (control).
Found by a blind dogfood run against
v0.1.90-21-gf56aa72.Symptom
A path that does not exist is reported as "a project root without a manifest", and exits 1 where the published contract says 2.
Two problems in one line:
exitCodeDocsdraws the split explicitly for a named local path that is not there: it exits 2 (a mistake about the invocation), not 1.A file that is not a directory is worse — a raw syscall error with a concatenated nonsense path:
This is the third copy of an inconsistency already fixed twice
The identical mistake, two commands, after #251:
Same class of error, same user mistake. One names the fix and exits 2; the other is bare and exits 1. #242, #245 and #251 each closed one copy of "a path failure is sorted by the wrong axis"; this is the remaining one, and it sits on the command a new author runs most.
AGENTS.mditem 24 is the governing context — the split is by the author's mistake, not by the errno.Suggested fix
In
app validate's path resolution,os.Statthe target before looking for the manifest and branch three ways:asUsageError(exit 2), message naming the next command (civitai app create, orcivitai app validate <dir>if they meant a different directory);asUsageError(exit 2), saying so plainly rather than surfacingstat X/block.manifest.json: not a directory;Keep the "directory with no manifest" case as-is: it is a genuine validation finding, not a usage error.
Test coverage this needs
Pin the exit code with
errors.Is(err, ErrUsage), never message text — perAGENTS.mditem 7, the sentinels carry no visible text, so a message assertion says nothing about the code. Measured on #251: an equivalent one-token mutation passed a green suite because every existing assertion was on wording.Rows: missing path → 2; a regular file → 2; a real project dir with no manifest → 1 (control, unchanged); a valid project → 0 (control).