Conversation
…e version that failed Both hand-built RolledBack entries put the version landed *on* in `to`: recover_on_start's boot-counter revert named the release just reverted to, and record_rescue named the golden the rescue moved to. The documented invariant is the opposite — a RolledBack entry's `to` is the version that failed, because Journal::known_bad reads it to keep rollbacks from landing on a bad release. The two entries therefore blacklisted the healthy release now running and never the one that failed: a later rollback would skip the good previous (escalating to golden or Stuck), and the scheduler's brick-loop guard never stopped the failed release being retried every check interval. recover_on_start now writes through Engine::record like every other outcome, so the entry cannot disagree with journal_outcome again; record_rescue names crumb.from. The rescue test had pinned the wrong semantics — fixed, and both it and the boot-exhaustion test now assert on known_bad directly. Assisted-by: Kimi:kimi-code
…mid-character The captured output passes through from_utf8_lossy, which pads invalid bytes into 3-byte U+FFFD runs, so a chatty hook can easily place a multi-byte character straddling the 8 KiB cap. String::truncate panics on that, inside Engine::apply: for a socket-triggered apply the connection died mid-update with no journal entry; for apply_unattended it killed the periodic-check task, silently disabling unattended updates until a restart. transcript.rs already cuts on a char boundary for the same reason; this brings the hook path in line. The regression test drives a hook whose output puts a two-byte é across byte 8192. Assisted-by: Kimi:kimi-code
…r the swap transition_to (select / rollback / reset-to-golden) answered a failed apply action by disarming the trial and returning early: no health gate, no rollback, no log entry. The board stayed on the unverified release with no boot-counter protection, and support saw nothing — while apply rolls back on the identical failure, and the function's own comment claims every class of outcome is journalled. A unit refusing to restart reaches this in production, which is what the new fail_apply_action injection stands in for; the forward direction only, since the revert's own apply action is already covered by fail_rollback_apply. Assisted-by: Kimi:kimi-code
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.
Three independent fixes in the updater, one commit each, every one with a regression test.
recover_on_startwrote its entries by hand instead of throughrecord(), andrecord_rescuejournalled the rescue againstcrumb.to— the version that booted — rather thancrumb.from, the one whose failure caused it. A later reader of the journal would pair the rescue with the wrong release.truncate(MAX)on captured hook output panics when the limit lands mid-character. Now it backs off to the boundary.transition_topropagated a post-swap apply-action failure with?, leaving the new slot swapped in with the gate unmet — the next start would boot a version whose apply never completed. The failure now joins the same rollback-and-journal path as a failed swap, with afail_apply_actionfault-injection switch covering it in tests.cargo test --workspace,clippy -D warningsandfmt --checkare all green; each commit also builds and passes on its own, so the branch bisects cleanly.