chore: convert production unwrap() to expect() with invariant messages - #198
Merged
Merged
Conversation
Mikola Lysenko (mikolalysenko)
enabled auto-merge (squash)
August 15, 2026 00:57
Jon (darakian)
approved these changes
Aug 15, 2026
Jon (darakian)
left a comment
There was a problem hiding this comment.
Looks good. 🤞this produces more usable logs over time
Sweep of every .unwrap() in production code paths (both crates): 111 sites across 12 files converted to .expect() whose message states the invariant that justifies the expectation, so a violated invariant panics with its reason instead of a bare unwrap message. Test-scope unwraps (cfg(test) modules, tests/, and the cfg(test)-gated conformance_tests.rs / yarn_layering_tests.rs) are deliberately left alone: a test panic already identifies the failing test, and the churn would drown review. Zero behavior change: no control-flow edits, no unwrap-to-? rewrites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
chore/unwrap-to-expect
branch
from
August 18, 2026 15:15
a44ce82 to
2bca69a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cc Jon (@darakian)
What
Audited every
.unwrap()in the repo (8,651 sites in tracked Rust files) and converted the 115 production-scope sites (13 files) to.expect()with a message stating the invariant that justifies the expectation — so if an invariant is ever violated, the panic says why the value was supposed to exist instead ofcalled Option::unwrap() on a None value.core/src/patch/redirect/mod.rsOptionscli/src/commands/*(9 files)--vex/identifier guard invariants,serde_json::Valueserialization infallibility, manifest-path parentscore/src/setup/composer/mod.rsparse_checkedobject-root guarantee, guard-insertedscriptskeycore/src/api/client.rsorg_slug.is_some()guards, single-org match armcore/src/vendor/go_sum_edit.rspeek()-guarded iteratornext()What was deliberately left alone
cfg(test)modules,tests/, and thecfg(test)-gatedvex/conformance_tests.rs/vendor/yarn_layering_tests.rs. Bareunwrap()is idiomatic there — a test panic already identifies the failing test — and converting them would drown the review in churn.unwrap→?rewrites, no control-flow edits. Sites that are genuinely fallible (below) were converted with an honest message rather than silently "fixed".How it was verified
#[cfg(test)]extent analysis) confirming no production.unwrap()was missed — including files where an earlycfg(test)item precedes more production code. No production.unwrap_err()/.unwrap_unchecked()exist anywhere.unwrap→expect+ rustfmt rewrap), and message truth — each claimed guard/capture group/infallibility was checked against the actual surrounding code and regex patterns. One inaccurate message was caught and corrected (setup.rsstdout flush — see below).cargo check --workspace --all-targets,cargo clippy(clean), full defaultcargo test --workspace(green),rustfmt --checkon all 13 files.Rebase 2026-08-18
Rebased onto current main (was based pre-#187). Conflicts came from #196's rewrite of the Cargo.toml pinning logic (which replaced
add_cargo_toml_registry— and 14 already-converted sites — withplan_cargo_toml), the yarn-classic CRLF work, the bundler CHECKSUMS CRLF tolerance, and composer'sserialize_like_input(which removed 2 convertedto_string_prettysites). Resolution kept main's code and re-applied the conversion. The same scope-aware sweep then found 19 new productionunwrap()s introduced on main (18 inredirect/mod.rsfrom #196 et al., 1 in the newvendor/go_sum_edit.rsfrom #173) — all converted in the same style, restoring the zero-production-unwraps invariant. Count moved 111 → 115 (−15 sites deleted by main's rewrites, +19 new). clippy--all-features -D warningsand defaultcargo test --workspacegreen after rebase.Follow-up findings (reported, not fixed here)
--manifest-path /panics — 4 sites (scan/mod.rs,rollback.rs,apply.rs,repair.rs) domanifest_path.parent().expect(…);resolved_manifest_path()passes an absolute flag value through verbatim, so a pathological--manifest-path /reachesparent() == None. Low severity, user-inflicted; the expect message now documents the intended invariant.setup.rsconfirm_proceedstdout flush is genuinely fallible (EPIPE on closed stdout at prompt time). Same failure class asprintln!panicking on closed stdout elsewhere in the CLI, so behavior is uniform — but it's a real condition, and the message describes the failure rather than claiming a false invariant.maven_tag_inner_rangeinterpolatestaginto a regex withoutregex::escape(redirect/mod.rs). All current callers pass literal tag names, so the invariant holds today; a defensiveregex::escape(tag)would future-proof it.🤖 Generated with Claude Code
Note
Low Risk
No logic changes; only panic message text on paths that were already expected to be unreachable or infallible. Residual edge cases (e.g. pathological
--manifest-path /) remain the same as before.Overview
Replaces 111 production
.unwrap()sites with.expect(...)so a violated assumption panics with a stated reason instead of a genericOption::unwrapmessage. Scope is 12 files in the CLI (apply,get,repair,rollback,scan,setup,output), core API client, patch redirect rewriters, and composer setup.Messages tie to local guards:
--vex/ identifierOptions, manifest file parents, infallible in-memoryserde_jsonserialization, static or escaped regex compilation, and regex capture groups that the pattern always defines. Test code is unchanged (~8.5kunwrap()left in tests).This is a mechanical refactor only—no
unwrap→?rewrites and no new error paths.Reviewed by Cursor Bugbot for commit a44ce82. Configure here.