Repository navigation
2.12.1 — a website, and Restore All no longer ends early when one step throws - #23
Merged
Merged
Conversation
Splitting the settings page into a page and five sections turned one loading flag into six independent checks, and neither half of what that flag does is enforced by anything any more. A handler that does not consult it fires while the page is filling its controls in, because assigning a property raises the same notification a click does. Opening the page then writes the settings file several times over and applies each value as though it had just been chosen - re-registering the hotkey, rewriting the schedule, toggling the tray icon. A section the page never loads is the mirror image: it shows its defaults instead of what is stored, then writes those defaults over the real settings the moment anything on it is touched. Nothing is broken today. All fifteen handlers do consult the flag - in three different spellings, because the page owns the field and a section reads it through the interface, and the schedule's three one-line handlers hand the decision to Apply(). That variety is the reason a check is worth having: it is held by convention across six files, and the sixth section is where convention runs out. So the check follows the flag rather than matching a name, and follows one step of delegation for the schedule's shape. Verified by making each failure real: one handler with its guard removed is named with its file, and a section dropped from the page's OnEnterAsync is named on its own.
Restorers run in order, so an exception in one took every later one with it - and the report built to make a silently skipped change visible was never returned at all. The user would be told the rollback failed, with nothing saying which half of it went through. A machine left mid-rollback is the worst state this application can put one in. No restorer is known to throw today; that was checked rather than assumed. Each one's underlying service answers with an OperationResult, including the hosts file, which Defender can refuse, and the registry value parser returns null on anything it cannot read rather than throwing. But they reach bcdedit, netsh, the registry, the task scheduler and PowerShell, and "everything down there reports rather than throws" is not a property this class can rely on. So a throw becomes what a refusal already is: counted against that restorer, named in its errors in the user's language, and not the end of the rollback. Cancellation still propagates - the user left the page, which is not a restorer failing.
A GitHub release page is where you go once you have already decided. This is the page for someone who has not: what it is, what it costs you, and a download button in the first screen. Built as static files under docs/, so GitHub Pages serves it from the same repository the code lives in - no build step, no framework, no third-party host, 232 KB for the whole site. It uses the application's own brand colours, and the hero carries a live frame-time strip because that is what the thing being downloaded is for. The SEO is the part that decides whether any of it is seen: per-language title, description and canonical, hreflang between the two, Open Graph and Twitter cards on the real screenshot, a sitemap, and JSON-LD for both SoftwareApplication and FAQPage - the second is what puts the answers themselves into search results. The version and file size are read from the GitHub API at load, with the markup's values as the fallback, so the page cannot go stale behind a release. Two real defects, found by rendering the page and looking at it rather than trusting that it was fine: The whole middle of the page was invisible. Every section was opacity:0 waiting for a scroll observer, so anything that stopped the script - a blocked request, an old browser, a crawler that does not run JavaScript - left a page with a hero, a footer and nothing between them. The reveal is an enhancement now: the content is visible on its own and only hides when scripting is confirmed present. The page scrolled sideways on a phone, 605px of it in a 390px window, because the hero's gradient field is deliberately wider than the viewport. It clips now, with overflow-x:clip rather than hidden so the sticky header survives, and the field is scaled down under 700px instead of merely being cut off. Checked at 1440 and 390 wide in both languages: no overflow, no console errors, no missing references, and both JSON-LD blocks parse.
The version was still v2.12.0, which shipped on 18 September. Merging on that number would not have failed - the workflow sees the existing tag and quietly replaces its assets - and that is the problem. The rollback fix in this branch is a real behaviour change, and it would have gone out under release notes that do not mention it, silently changing the binary and its checksum underneath anyone who had already downloaded and verified it. RELEASING.md names that trade: a published release that changes underneath people is worse than a version gap. So the number moves instead. Patch rather than minor: the application gains a fix and nothing else. The website is an addition, but it is not shipped inside the executable.
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.
What this changes
A landing page at anton-babaskin.github.io/SysTuneX,
in English and Russian, with the download button in the first screen. And the rollback path no
longer gives up halfway when one restorer throws.
Why
Restore All could end early and say nothing useful. Restorers run in order, so an exception in
one took every later one with it — and the
RollbackReport, which exists specifically to say whichchanges came back and which did not, was never returned at all. The user would be told the rollback
failed, with nothing saying which half went through. A machine left mid-rollback is the worst state
this application can put one in.
Nothing is known to throw today; that was checked rather than assumed.
UnblockTelemetryHostsAsynccatches and returns an
OperationResult, including the Defender refusal the release notes alreadydocument, and
RegistryValueComparer.MaterializeusesTryParsethroughout and returns null ratherthan throwing on a journal entry it cannot read. But the restorers reach bcdedit, netsh, the
registry, the task scheduler and PowerShell, and "everything down there reports rather than
throws" is not a property the rollback can rely on. A throw is now what a refusal already was:
counted against that restorer, named in the user's language, and not the end of the rollback.
Cancellation still propagates — the user left the page, which is not a restorer failing.
The settings page can no longer enforce two of its own rules. Splitting it into a page and five
sections in 2.12.0 turned one loading flag into six independent checks. A handler that does not
consult it fires while the page is filling its controls in and writes the settings file several
times over, applying each value as though it had just been chosen. A section the page never loads
shows its defaults and then writes those over the real settings. Nothing is broken today — all
fifteen handlers do consult the flag, in three different spellings — and that variety is exactly why
a check is worth having.
How it was verified
dotnet build SysTuneX.sln -c Release— no warningsdotnet test tests/SysTuneX.Core.Tests— 461 green, up from 456dotnet test tests/SysTuneX.App.Tests— Windows only; CI runs itEach behavioural claim was checked by breaking the code and confirming the right test failed:
A_restorer_that_throws_does_not_stop_the_ones_after_it— the later restorer still ranCancellation_is_not_recorded_as_a_failureOnEnterAsyncTwo real defects in the website, found by rendering it rather than trusting it:
opacity:0waiting for ascroll observer, so anything that stopped the script — a blocked request, an old browser, a
crawler that does not run JavaScript — left a hero, a footer and nothing between them. For a page
whose entire job is being found in search, that is the worst possible failure. The reveal is an
enhancement now: content is visible on its own and only hides once scripting is confirmed.
gradient field is deliberately wider than the viewport. It clips now, with
overflow-x:cliprather than
hiddenso the sticky header survives, and the field scales down under 700px.Also checked: no console errors, no missing local references, and both JSON-LD blocks parse.
Risk and rollback
the change.
docs/. It ships nothing inside the executable and cannotaffect a running install.
Release
release.versionbumped tov2.12.1, andCHANGELOG.mdhas a## v2.12.1sectionStrings.resx,Strings.ru.resxandStrings.uk.resx— the rollback failure message is translated in all threeWhy the number moved.
release.versionwas stillv2.12.0, which shipped on 18 September.Merging on that number would not have failed — the workflow sees the existing tag and quietly
replaces its assets with
--clobber. That is the problem: a real behaviour change would have goneout under release notes that do not mention it, silently changing the binary and its checksum
underneath anyone who had already downloaded and verified it. RELEASING.md names that trade
directly, so the number moves instead.
After merging, GitHub Pages needs turning on once: Settings → Pages → Deploy from a branch →
main//docs. Until then the README links point at a 404.Generated by Claude Code