Repository navigation
Make every --json top-level error a {code, message} object (#704) - #1027
Mikola Lysenko (mikolalysenko) wants to merge 13 commits into
Conversation
f90a870 to
70c3229
Compare
|
[agent] CI status: all checks green on 70c3229 (555 pass, 6 skipped). Fixed: CodeQL rust/cleartext-logging (the forced --id/--cve/--ghsa shape error no longer echoes the argument); remove/scan tests now assert the coded usage error on stdout under --json; json_error_shape.rs spawns via hermetic::binary_command (spawn_env_hygiene). These failed on Linux too, not only Windows. Rebased onto origin/main (CLI_CONTRACT.md rollback table conflict: kept both the new error row and main's warnings wording). macOS/sbt/mill/coverage-merge failures were runner/apt infra and passed on rerun. Generated by Claude Code |
set_error / set_error_keep_status write the top-level error as a
{code, message} object and drop any top-level errorCode. legacy_error and
print_legacy_error give scan, get and rollback the minimal
{status: "error", error: {code, message}} shape. usage_error prints a
self-enforced usage error under --json (a full envelope for envelope
commands, the legacy shape for scan/get/rollback), or Error: on stderr,
and returns 2.
Guard tests fail on a bare `return 2;` in src/commands (outside the
hidden hosted_bundle harness) and on a "status": "error" JSON literal
whose top-level error is a string or carries errorCode.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
) get: report_error takes a code (patch_fetch_failed, manifest_unreadable, manifest_write_failed, patch_no_applicable_files, offline_unsupported); the lock failure, the nested-apply run error, blob_write_failed and selection_required carry error: {code, message}; top-level errorCode is gone. The nested-apply error keeps status partial_failure. scan: one hosted emitter that requires a code (refusals, lock_held/ lock_io, patch_details_failed, reference_resolve_failed, lockfile_write_failed); discovery failure, --offline, all-batches-failed (api_batch_failed), embedded VEX failure, the vendor step and the socket.yml refusal all write the object. rollback: emit_rollback_error takes a code; manifest_not_found, manifest_invalid, manifest_unreadable, patch_not_found, path_glob_no_match, hosted_wiring_contested, vendor_ledger_missing and rollback_failed. Usage errors (exit 2) in scan, get, rollback, remove, repair, vendor and vex go through json_envelope::usage_error, so under --json they print the coded error on stdout. Breaking change to the v5.0 JSON contract. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…704) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…704) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…704) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The contract's nested-apply note said the v5 shape replaced a "top-level error.code + string error pair"; the replaced pair was the top-level errorCode. Three test doc comments still described the hosted lock_held/lock_io envelope as a top-level errorCode with a string error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- get: the forced --id/--cve/--ghsa shape error no longer echoes the argument. With usage_error printing it, CodeQL traced a test's patch uuid into eprintln (rust/cleartext-logging). - remove/scan tests: under --json the self-enforced usage error is now the coded error on stdout, so assert it there instead of on stderr. - json_error_shape.rs spawns through hermetic::binary_command, as spawn_env_hygiene requires. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
70c3229 to
a458ba9
Compare
|
[agent] CI status: rebased onto main (05ecc6e) and force-pushed as a458ba9. One conflict, in CI: 555 checks passed, 6 skipped. The first run was cancelled partway through and I re-ran it. One check is still red: Generated by Claude Code |
Resolve the CLI_CONTRACT.md and socket.yml policy test conflicts keeping
both sides: main's new hosted rows (vlt workspace roots, uv/Poetry
takeover gates, per-purl `patches`, path-flag validation) stay, with
top-level errors described as the {code, message} object. Point the new
vlt workspace-member test at error.message, fix the stale top-level
errorCode wording for eject_refused and managed_install, and note that
the pre-dispatch path-flag check, like clap's parse errors, prints
nothing on stdout under --json.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolve the CLI_CONTRACT.md hosted-scan paragraph conflict: keep main's
new gem refusal codes and describe lock_held / lock_io as the
error: {code, message} object.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
[agent] Merged main, CI green; ready for review. Generated by Claude Code |
|
bugbot run Generated by Claude Code |
The path_glob_invalid usage error capitalized the message before handing it to usage_error, so the --json error.message was capitalized too. capitalize_first is a human-only transform: every other rollback JSON error (emit_rollback_error) and scan's path_glob_invalid keep the verbatim message. Capitalize only for the stderr line, and pin both forms in the json_error_shape test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LD4qUfZKhg2qgt3x9vGeFf
|
bugbot run Generated by Claude Code |
Resolve the conflicts with main's staged hosted takeover. hosted.rs now
writes through main's commit_hosted_writes; its failure path keeps the
PR's {code, message} error object and reports lockfile_write_failed.
CLI_CONTRACT.md takes main's staged-takeover text and changes the hosted
lock-failure JSON back to the PR's error object (no top-level errorCode).
Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
|
Burn-down agent: labeled Ready for review at 6eb0448.
Generated by Claude Code |
Main (#1043) moved get's agent-mode engine into agent_download.rs, so the {code, message} error object is ported there: report_error takes a code, report_lock_failure and the manifest read/write failures use legacy_error, and fold_apply_failures sets the run-level error with set_error_keep_status instead of a top-level errorCode. Rollback's path_glob_invalid keeps the verbatim JSON message and uses main's ui::sentence_case for the human line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 9a15a0b. Configure here.
Main's new contract rows (workspace-lock-elsewhere, eject refusals) and the stray-member-lock test (#1095) now use the top-level error object: the rows say error.code, and the test reads error.message. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What and why
On 2026-10-07 the maintainer chose option 1 on #704 ("Option 1 is clearly the correct choice. Implement proposed solution."). Every
--jsonfailure now writes the top-levelerroras a{code, message}object, the same shape asEnvelopeError. This is a breaking change for v5.0. It also covers register row C53: usage errors (exit 2) follow rule (b), so under--jsonthey print a coded error on stdout.Changes
src/json_envelope.rs:set_error,set_error_keep_status,legacy_error/print_legacy_error, andusage_error(cmd, json, code, msg) -> i32.--json,usage_errorprints a full envelope for apply, list, remove, repair, vendor and vex, and{status, error: {code, message}}for scan, get and rollback.--json, it printsError: …on stderr as before.return 2;undersrc/commands(hosted_bundle.rs is exempt), and on a"status": "error"JSON literal whoseerroris a string or has anerrorCodenext to it.report_errortakes a code. The lock failure, the nested-apply error,blob_write_failedandselection_requiredall use the object form, and the top-levelerrorCodeis gone.patch_no_applicable_files,offline_unsupportedandidentifier_invalid.status: "partial_failure".--offline,api_batch_failed, the embedded VEX failure, the vendor step andpolicy_error_jsonall use the object form.usage_error, with these codes:invalid_args,global_scope_unsupported,path_not_directory,path_glob_no_match,path_glob_invalid,path_outside_repoandinvalid_env.reference_resolve_failedandlockfile_write_failed.emit_rollback_error(json, code, msg)is now used for every failure, and the inline objects usemanifest_not_found,patch_not_foundandrollback_failed. A bad glob is a usage error (path_glob_invalid). When a path pattern matches no patched packages, rollback reuses scan'spath_glob_no_match.usage_errorand keep their existing codes.What users see
.erroras a string or read the top-level.errorCodemust now read.error.messageand.error.code.scan,removeandrollbackunder--jsonnow print a coded JSON error on stdout, not just text on stderr. Under--json, the message goes only to stdout.--global --mode vendoredgiveserror.code: "global_scope_unsupported"on scan, get and vendor.Docs
CLI_CONTRACT.md:errorkey.lock_held/errorCodemention is updated.2row states the stdout rule.jqrecipe for.error.code.docs/migrating-to-v5.mdhas a new "JSON output" section.CHANGELOG.mdis not touched.Tests
tests/json_error_shape.rscovers usage errors under--jsonfor scan, get, rollback, remove, repair and vex. It also covers stdout staying empty without--json, the offline refusals, and rollback'smanifest_not_found/patch_not_found.scripts/backtest-pipenv.py, which comparederroragainst a string.cargo test -p socket-patch-clilib tests (868).cli_parse_*suites, plus cli, get, scan and rollback.cargo clippyadds no new warnings.Review
The review found no correctness bugs. It fixed one wording error in the contract's nested-apply note and stale
errorCodecomments in the covgap get, scan_hosted and rollback tests.These are left as is because they predate this branch:
Envelope(command: "rollback"), not the{status, error}shape. Itserroris already{code, message}.run_scanpicksglobal_scope_unsupportedby matching the error message fromresolve_mode_flags. This is fragile. Sibling Decide: warn on and then remove scan --apply/--vendor, and whether --vex stays embedded #966 rewrites the same spot, so expect a conflict inscan/mod.rs.Closes #704
🤖 Generated with Claude Code
Note
Medium Risk
Breaking JSON contract for all automation that read string
erroror top-levelerrorCode; behavior is intentional v5.0 and heavily tested, but downstream scripts must migrate.Overview
v5.0 breaking change: every
--jsonfailure now uses a single top-levelerror: {code, message}object (theEnvelopeErrorshape). The old stringerrorand siblingerrorCodeare removed.Shared helpers in
json_envelope.rs(legacy_error,set_error,usage_error, etc.) centralize emission soget,scan(hosted/policy/vendor),rollback,remove,repair,vendor, andvexcannot drift. Usage errors (exit 2) under--jsonnow print a coded object on stdout for legacy commands (scan,get,rollback) as well as envelope commands. Nested apply failures keeppartial_failurewhile settingerror.code.getadds stable codes (patch_no_applicable_files,offline_unsupported,identifier_invalid, …) and stops echoing forced-identifier typos in messages (CodeQL).CLI_CONTRACT.mddocuments the unified shape, rollback'serrorkey, expanded code tables, and jq recipes. Tests and backtest scripts were updated (~70 assertions); newtests/json_error_shape.rsguards the contract.Reviewed by Cursor Bugbot for commit 9a15a0b. Configure here.
Generated by Claude Code