You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A directory at .socket/manifest.json exits 2 with no --json output when --manifest-path spells the path, but exits 1 with manifest_unreadable under the default path #1123
Kind: bug. Source: new finding, register C76. It's a regression from #1029 and a sixth copy of the manifest-shape rule in C56 (#998).
Problem
#1029 added GlobalArgs::validate_paths, which runs once in main.rs#L81-L84 before dispatch. It refuses a manifest that is a directory, but only when the string passed to --manifest-path differs from the default:
ifself.manifest_path != DEFAULT_PATCH_MANIFEST_PATH{let manifest = self.resolved_manifest_path();if manifest.is_dir(){returnErr(..."is a directory, not a manifest file")}
So the same file on disk gets two verdicts, depending on how the path is spelled:
Explicit spelling (./.socket/manifest.json, an absolute path, or SOCKET_MANIFEST_PATH): exit 2 from main. It prints a bare Error: line on stderr and nothing on stdout, even under --json.
Default spelling: the command runs. read_manifest then refuses the non-regular file, and each command maps that error its own way.
The error message also doubles the prefix: `././.socket/manifest.json`, because cwd. is joined onto ./.socket/….
Proof by execution. A debug build at b96a785, run twice under env -i with --json --offline, with identical results. The fixture is mkdir -p .socket/manifest.json:
command
default path
--manifest-path ./.socket/manifest.json
list
exit 1, manifest_unreadable
exit 2, no JSON
apply
exit 1, apply_failed
exit 2, no JSON
apply --check
exit 1, manifest_unreadable
exit 2, no JSON
remove pkg:npm/a@1
exit 1, manifest_unreadable
exit 2, no JSON
rollback
exit 1, error string ./.socket/manifest.json is not a regular file
exit 2, no JSON
vendor --check
exit 1, manifest_unreadable
exit 2, no JSON
vex -O /dev/null
exit 2, manifest_unreadable
exit 2, no JSON
A CI job therefore changes its exit code and loses its --json document depending on whether it spells out the default path.
Symptoms
None filed. Related: #998, where stat errors on the manifest are read as "no manifest"; #931, five codes for one unreadable manifest; #704, where exit-2 usage errors under --json use stderr only.
Impact: low to medium. This is not data loss, but it is a CI-gate verdict that depends on spelling, the same class of defect as the trust-signal fixes in #1029.
Proposed change
validate_paths should validate only what makes the flag a usage error, and do it by resolved path, not by string.
Option A (recommended): drop the is_dir check from validate_paths. A directory manifest is a corrupt-state error, not a usage error, and read_manifest already refuses it for every spelling. Keep the "project directory does not exist" check, which really is about the flag.
Option B: compare resolved_manifest_path() with cwd.join(DEFAULT_PATCH_MANIFEST_PATH) (lexically normalized) instead of comparing strings, and run the directory check for both. This makes the default path exit 2 as well, which is a contract change for manifest_unreadable.
Either way, the error message should print the normalized path (utils::relpath::normalize_lexically), not ././….
Size and scope
Files: crates/socket-patch-cli/src/args.rs (validate_paths, about 10 lines) and its unit tests. With option B, also the contract's exit-2 row.
list, apply, apply --check, remove, rollback, vendor --check and vex give the same exit code and --json output for a directory at .socket/manifest.json, whether the path is the default, ./.socket/manifest.json, an absolute path or SOCKET_MANIFEST_PATH.
A regression test parameterized over those four spellings, in tests/ through the hermetic builder.
The existing validate_paths tests stay green: a missing --cwd, a missing --global-prefix, and a manifest in a project directory that doesn't exist.
Error messages show a normalized path.
Dependencies
None. This lands independently of #998 and #931. If #704's usage_error helper lands first, option B's exit 2 should go through it.
[agent] Triaged as priority:p3 (general CLI). Confirmed on main: crates/socket-patch-cli/src/args.rs:434 runs the directory check only when the --manifest-path string differs from DEFAULT_PATCH_MANIFEST_PATH. Related to #998 and #931, but as the report says, the fix here is local to validate_paths, so it isn't clustered with them. No open PR addresses it yet.
v5 triage: P3, not a release blocker. A directory deliberately occupying manifest.json is malformed input. Retain P3 diagnostic consistency work, not a v5 gate.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register comment.
Kind: bug. Source: new finding, register C76. It's a regression from #1029 and a sixth copy of the manifest-shape rule in C56 (#998).
Problem
#1029 added
GlobalArgs::validate_paths, which runs once inmain.rs#L81-L84before dispatch. It refuses a manifest that is a directory, but only when the string passed to--manifest-pathdiffers from the default:So the same file on disk gets two verdicts, depending on how the path is spelled:
./.socket/manifest.json, an absolute path, orSOCKET_MANIFEST_PATH): exit 2 frommain. It prints a bareError:line on stderr and nothing on stdout, even under--json.read_manifestthen refuses the non-regular file, and each command maps that error its own way.The error message also doubles the prefix:
`././.socket/manifest.json`, becausecwd.is joined onto./.socket/….Proof by execution. A debug build at
b96a785, run twice underenv -iwith--json --offline, with identical results. The fixture ismkdir -p .socket/manifest.json:--manifest-path ./.socket/manifest.jsonlistmanifest_unreadableapplyapply_failedapply --checkmanifest_unreadableremove pkg:npm/a@1manifest_unreadablerollback./.socket/manifest.json is not a regular filevendor --checkmanifest_unreadablevex -O /dev/nullmanifest_unreadableA CI job therefore changes its exit code and loses its
--jsondocument depending on whether it spells out the default path.Symptoms
None filed. Related: #998, where stat errors on the manifest are read as "no manifest"; #931, five codes for one unreadable manifest; #704, where exit-2 usage errors under
--jsonuse stderr only.Impact: low to medium. This is not data loss, but it is a CI-gate verdict that depends on spelling, the same class of defect as the trust-signal fixes in #1029.
Proposed change
validate_pathsshould validate only what makes the flag a usage error, and do it by resolved path, not by string.is_dircheck fromvalidate_paths. A directory manifest is a corrupt-state error, not a usage error, andread_manifestalready refuses it for every spelling. Keep the "project directory does not exist" check, which really is about the flag.resolved_manifest_path()withcwd.join(DEFAULT_PATCH_MANIFEST_PATH)(lexically normalized) instead of comparing strings, and run the directory check for both. This makes the default path exit 2 as well, which is a contract change formanifest_unreadable.Either way, the error message should print the normalized path (
utils::relpath::normalize_lexically), not././….Size and scope
crates/socket-patch-cli/src/args.rs(validate_paths, about 10 lines) and its unit tests. With option B, also the contract's exit-2 row.--jsonchannel for exit 2 (Decide: one shape for the--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704).Acceptance criteria
list,apply,apply --check,remove,rollback,vendor --checkandvexgive the same exit code and--jsonoutput for a directory at.socket/manifest.json, whether the path is the default,./.socket/manifest.json, an absolute path orSOCKET_MANIFEST_PATH.tests/through the hermetic builder.validate_pathstests stay green: a missing--cwd, a missing--global-prefix, and a manifest in a project directory that doesn't exist.Dependencies
None. This lands independently of #998 and #931. If #704's
usage_errorhelper lands first, option B's exit 2 should go through it.