Repository navigation
fix: self update --status and --recover refuse another spelling of the same OCM_HOME or HOME - #901
Conversation
…e same OCM_HOME or HOME
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs maintainer review before merge. What this changesThis PR accepts equivalent HOME and OCM_HOME paths for self-update status and recovery and gives retained workers the recorded spellings. Example: An interrupted update records /tmp/ocm-repro/store, while /tmp/ocm-repro/store-link points to that directory.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness✅ Ready for maintainer review Keep open: this maintainer-authored PR fixes a recovery defect still present on main, and no blocking patch findings remain. Priority: P2 Before mergeNone. FindingsNone. Tests
Agent review detailsHow this fits togetherThe self-update transaction layer validates the caller against a private receipt beside the installed executable, then delegates rollback and service restoration to the retained previous executable. flowchart TD
A[Status or recovery command] --> B[Read private receipt]
B --> C[Compare recorded and current paths]
C --> D[Reject different directories]
C --> E[Return update status]
C --> F[Start retained worker with recorded paths]
F --> G[Restore executable and services]
Technical reviewBest possible solution: Use the shared directory-identity rule while preserving recorded receipt paths for older recovery workers. Do we have a high-confidence way to reproduce the issue? Current main directly compares receipt paths, and #899 supplies concrete Linux and macOS reproductions; this review executed no target code. Is this the best way to solve the issue? The patch addresses the comparison at its existing owner and preserves older-worker compatibility without changing path resolution globally or introducing another identity mechanism. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 88185eee3cfa. Provenance checked
TestingProof path: shipped entry point. SecurityNone. EvidenceWhat I checked:
Likely related people:
LabelsLabel changes:
Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment HistoryReviewed October 31, 2026, 12:31 AM ET / 04:31 UTC (Revision 3). |
… worker is an older release
|
Fixed in 515f6b0: the recovery worker now gets the I reproduced the failure before fixing it. A temporary variant of The committed test now also checks that the rollback worker's service calls use the recorded @clawsweeper re-review |
|
🦞👀 Re-review progress:
|
|
1dd8f6c adds test coverage for the other half of the worker handoff and changes no behavior. The integration test couldn't catch a missing @clawsweeper re-review |
|
🦞👀 Re-review progress:
|
|
Fixed: an interrupted or finished self-update can now be inspected and recovered through any spelling of the same OCM_HOME or HOME, while a different store is still refused. |
Fixes #899
What Problem This Solves
Fixes:
ocm self update --statusandocm self update --recoverrefuse an update whenOCM_HOMEorHOMEis reached through a different spelling of the same directory than the one the update was started with.ocm self updaterecords theOCM_HOMEandHOMEit ran with in the update receipt next to the installed binary. Before--statusor--recoverdoes anything, it checks that the current store andHOMEare the ones in the receipt. That check compared the two paths as text, so a symlinked store, a symlinkedHOME,/tmpversus/private/tmpon macOS, or the absolute path of anOCM_HOMEthat was relative when the update started all looked like a different store. OCM then printedself-update belongs to a different HOME or OCM_HOME; use the original storeand stopped.The case that hurts is an interrupted update. The installed binary has already been swapped for the candidate, the receipt says
applying, and the update tells the user to run--recover. From another spelling of the same store,--recoverwas refused until the user found the exact original spelling, which the error does not show. See #899 for the full report.User Impact
User impact: an update started through one spelling of the store can be inspected with
--statusand rolled back with--recoverthrough any other spelling of the same store orHOME.What stays the same:
HOMEthat is a different directory, missing, or cannot be resolved is still refused with the same message, and the refused--recoverchanges nothing: the receipt staysapplyingand the candidate stays installed.OCM_HOMEandHOME.ocm self update --checkand starting a new update do not read this check and are unchanged.Why This Change Was Made
#892 fixed the same class of problem for service and supervisor ownership by adding
same_ocm_homeinsrc/store/layout.rs: a recorded path names the current one when the text matches, or when both are absolute and resolve to the same existing directory. A relative, missing or unresolvable recorded path never matches another spelling. The self-update receipt check was not part of that change (#881 mentioned it as a side note).The change has two parts in
src/cli/self_update_transaction.rs:validate_receipt_homeuses that same rule for both the recordedOCM_HOMEand the recordedHOME, instead of!=. Using the existing function keeps one definition of "same store" for services, the supervisor and self-update, and inherits its refusals for missing and unresolvable paths. The receipt stores paths as JSON strings, so they are always valid UTF-8; a current path that is not UTF-8 falls back to the previous exact comparison.previousbinary, which after an upgrade is the older release. Older releases still compare the receipt's directories as text. If the worker inherited the caller's alias spelling, a recovery started from the new binary would pass the new check, mark the receiptrollingBack, and then be refused by the old worker before anything was rolled back.spawn_workernow starts the worker with theOCM_HOMEandHOMEthe receipt recorded, so a worker of any version sees the exact spelling it expects. These are the same directories the caller was validated against, and the worker already switched to the recordedOCM_HOMEbefore doing any work, so the store it acts on is unchanged.Evidence
Before the fix, on main at f67a89d, a scripted run of the real CLI (isolated
HOME,OCM_HOMEandTMPDIR, stubbed service managers, a loopback release fixture with a fake9.9.9candidate, a private copy of the binary) reproduced the report on Linux x86_64 and macOS arm64. With the original spelling,--statussucceeded and--recoverof an interrupted update rolled it back. Every alias was refused with the message above: a symlinked store, a symlinkedHOMEwith an explicitOCM_HOME, a symlinkedHOMEwith the default store, the absolute symlink path of a relativeOCM_HOME,/tmpfor a/private/tmpstore (macOS only), and--statusand--recoverof an interrupted (applying) update through a symlinked store. After the refused--recover, the receipt was stillapplyingand the candidate was still installed.After the fix, the same script on this branch, on both platforms:
--status(absolute, relative, default store)--statusHOME(explicitOCM_HOME, and default store),--statusOCM_HOME,--status/tmpspelling of a/private/tmpstore,--status(macOS)--statusthrough a symlinked storeapplying--recoverthrough a symlinked storeapplyingrolledBack, original binary restored, receipt keeps its recordedocmHomeHOME,--statusand--recover--statusand--recover--checkRegression test.
local_transaction_survives_gateway_teardown_and_recovers_failuresintests/self_update_transaction_tests.rsalready interrupts an update started with a relativeOCM_HOMEand checks that a missingHOMEorOCM_HOMEis refused. It now also:--statusthrough a symlinked store, the absolute path of the relativeOCM_HOME, and a symlinkedHOME, each showing the sameapplyingupdate without touching services;HOME, checks that the rollback worker's service calls used the recordedHOMErather than the alias, and then checks the existing assertions:rolledBack, same receipt id, recordedocmHomeunchanged, original binary restored, no nested store created, and a replayed--recoverchanges nothing.This test passed on this branch on Linux (320 s) and macOS (241 s). Against the unchanged source it fails on the first alias
--status.A unit test in
src/cli/self_update_transaction.rschecks the worker's start command directly: given a caller whoseOCM_HOMEandHOMEare other spellings, the worker gets the receipt's recorded values. The integration test can't catch a missingOCM_HOMEoverride on its own, because a worker built from the same source accepts the alias and then switches to the recordedOCM_HOMEitself. With that override removed, the unit test fails.Recovery with an older retained worker. The test above builds both binaries from this branch, so it can't show what happens when the retained worker is an older release. To check that, a temporary, uncommitted variant of the same test builds the binary that starts the update, which becomes the retained worker, from main at f67a89d. Its receipt check is the same text comparison as in v0.3.8. The
9.9.9candidate is built from this branch. On Linux:--recoverleft the receipt atrollingBackwith the candidate still installed, because the older worker refused the alias.Other checks on the exact commit:
git diff --check,cargo check --workspace --all-targets --locked(Linux), andcargo fmt --check(macOS) passed, and the scripted runs above were repeated on this revision on both platforms with the same results. Hosted CI runs the full suite, including Linux and macOS.Limits. The scripted runs used a fake candidate binary and no installed OCM service, so they don't exercise service restarts through an alias. The integration test does cover recovery with its stand-in service manager. The older-worker check used main's source rather than the published v0.3.8 binary; their receipt check and worker launch are the same. Windows does not support recoverable self-update and was not run.