diff --git a/.gitignore b/.gitignore index fee4cc8..16abd70 100644 --- a/.gitignore +++ b/.gitignore @@ -23,26 +23,37 @@ uninstall.bat # Keep release documentation tracked !docs/release/ !docs/release/*.md +!docs/releases/ +!docs/releases/*.md # Local worktrees and planning docs .worktrees/ docs/superpowers/plans/ +/FINAL_RELEASE_PLAN*.md +/RELEASE_PLAN*.md +/LOCAL_VALIDATION*.md +/VALIDATION_NOTES*.md +/PR*_VALIDATION*.md +/CHOCOLATEY_MODERATOR_NOTICE*.md # Test and coverage [Tt]est[Rr]esult*/ +**/[Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *.VisualState.xml TestResult.xml nunit-*.xml +coverage/ +coverage-report/ +coverage-results/ +lcov-report/ coverage*.json coverage*.xml coverage*.info +lcov.info *.coverage *.coveragexml *.trx -FINAL_RELEASE_PLAN.md -RELEASE_PLAN.md -CHOCOLATEY_MODERATOR_NOTICE.md # .NET / NuGet *.deps.json @@ -92,17 +103,37 @@ $RECYCLE.BIN/ *.log logs/ Logs/ +*.pid +*.trace +*.stackdump .DS_Store .fuse_hidden* .directory .Trash-* +/screenshots/ +/validation-screenshots/ +/threadpilot-menu-validation/ +*-screenshot.png +*-screenshot.jpg +*-validation.png # Installer / packaging artifacts Installer/Output/ +/release/ +/release-build/ +/package-stage/ +/_stage*/ *.cab *.msi *.msm *.msp +*.msix +*.msixbundle +*.appinstaller +*.blockmap +*.sha256 +*.sha512 +ThreadPilot-*.zip *.lnk *.exe.config *.[Pp]ublish.xml @@ -131,7 +162,14 @@ Configuration/ProcessProfiles.json Configuration/*.local.json Data/ AppData/ +appdata/ %AppData%/ThreadPilot/ +/settings.json +/core_masks.json +/persistent_rules.json +/Configuration/ +/Profiles/ +/PowerPlans/ # Project-specific extras .claude/ diff --git a/App.xaml.cs b/App.xaml.cs index d53c260..9aa438f 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -71,6 +71,8 @@ protected override void OnStartup(StartupEventArgs e) #if DEBUG bool isTestMode = false; #endif + bool effectiveStartMinimized = false; + ApplicationSettingsModel? loadedSettings = null; foreach (var arg in e.Args) { @@ -103,6 +105,8 @@ protected override void OnStartup(StartupEventArgs e) } } + effectiveStartMinimized = startMinimized; + // Set up global exception handlers first AppDomain.CurrentDomain.UnhandledException += this.OnUnhandledException; this.DispatcherUnhandledException += this.OnDispatcherUnhandledException; @@ -219,6 +223,8 @@ protected override void OnStartup(StartupEventArgs e) Task.Run(async () => await settingsService.LoadSettingsAsync()).GetAwaiter().GetResult(); var settings = settingsService.Settings; + loadedSettings = settings; + effectiveStartMinimized = startMinimized || settings.StartMinimized; var useDarkTheme = settings.HasUserThemePreference ? settings.UseDarkTheme : themeService.GetSystemUsesDarkTheme(); @@ -237,6 +243,7 @@ protected override void OnStartup(StartupEventArgs e) } var mainWindow = this.ServiceProvider.GetRequiredService(); + this.MainWindow = mainWindow; // Handle startup behavior with comprehensive error handling try @@ -249,23 +256,33 @@ protected override void OnStartup(StartupEventArgs e) throw new InvalidOperationException("MainWindow could not be created"); } - var startupWindowBehavior = StartupWindowBehavior.Resolve(isAutostart, startMinimized); + var startupWindowBehavior = StartupWindowBehavior.Resolve(isAutostart, effectiveStartMinimized); + var showStartupSuggestion = loadedSettings != null + && StartupMinimizedSuggestionPolicy.ShouldShow(loadedSettings, startupWindowBehavior); + mainWindow.ConfigureStartupMode( + isSilentStartupMode: !startupWindowBehavior.ShouldShowWindow, + showStartupMinimizedSuggestionOnReady: showStartupSuggestion); + mainWindow.ShowInTaskbar = startupWindowBehavior.ShowInTaskbar; mainWindow.Visibility = startupWindowBehavior.Visibility; mainWindow.WindowState = startupWindowBehavior.WindowState; - mainWindow.Show(); - if (startupWindowBehavior.HideAfterShow) + if (startupWindowBehavior.ShouldShowWindow) { - mainWindow.Hide(); - } - else if (startupWindowBehavior.ActivateAfterShow) - { - mainWindow.EnsureDashboardVisibleOnScreen(); - mainWindow.Activate(); + mainWindow.Show(); + + if (startupWindowBehavior.HideAfterShow) + { + mainWindow.Hide(); + } + else if (startupWindowBehavior.ActivateAfterShow) + { + mainWindow.EnsureDashboardVisibleOnScreen(); + mainWindow.Activate(); + } } - logger.LogInformation("Main window displayed successfully"); + logger.LogInformation("Startup window behavior applied successfully"); } catch (Exception ex) { @@ -450,4 +467,3 @@ private void ReportUnhandledException(Exception exception, string source, LogLev } } } - diff --git a/Helpers/StartupMinimizedSuggestionPolicy.cs b/Helpers/StartupMinimizedSuggestionPolicy.cs new file mode 100644 index 0000000..8036392 --- /dev/null +++ b/Helpers/StartupMinimizedSuggestionPolicy.cs @@ -0,0 +1,17 @@ +namespace ThreadPilot.Helpers +{ + using System; + using ThreadPilot.Models; + + public static class StartupMinimizedSuggestionPolicy + { + public static bool ShouldShow(ApplicationSettingsModel settings, StartupWindowBehavior behavior) + { + ArgumentNullException.ThrowIfNull(settings); + + return behavior.ShouldShowWindow + && !settings.StartMinimized + && !settings.HasSeenStartupMinimizedSuggestion; + } + } +} diff --git a/Helpers/StartupWindowBehavior.cs b/Helpers/StartupWindowBehavior.cs index f78ae73..642b66a 100644 --- a/Helpers/StartupWindowBehavior.cs +++ b/Helpers/StartupWindowBehavior.cs @@ -3,6 +3,7 @@ namespace ThreadPilot.Helpers using System.Windows; public readonly record struct StartupWindowBehavior( + bool ShouldShowWindow, bool ShowInTaskbar, Visibility Visibility, WindowState WindowState, @@ -14,24 +15,27 @@ public static StartupWindowBehavior Resolve(bool isAutostart, bool startMinimize if (isAutostart && startMinimized) { return new StartupWindowBehavior( + ShouldShowWindow: false, ShowInTaskbar: false, Visibility: Visibility.Hidden, WindowState: WindowState.Minimized, - HideAfterShow: true, + HideAfterShow: false, ActivateAfterShow: false); } if (startMinimized) { return new StartupWindowBehavior( - ShowInTaskbar: true, - Visibility: Visibility.Visible, + ShouldShowWindow: false, + ShowInTaskbar: false, + Visibility: Visibility.Hidden, WindowState: WindowState.Minimized, HideAfterShow: false, ActivateAfterShow: false); } return new StartupWindowBehavior( + ShouldShowWindow: true, ShowInTaskbar: true, Visibility: Visibility.Visible, WindowState: WindowState.Normal, diff --git a/Installer/Installer.iss b/Installer/Installer.iss index ff61775..8beead0 100644 --- a/Installer/Installer.iss +++ b/Installer/Installer.iss @@ -5,7 +5,7 @@ #define MyAppPublisher "ThreadPilot" #define MyAppURL "https://github.com/" #define MyAppExeName "ThreadPilot.exe" -#define MyAppVersion "0.1.0-beta" +#define MyAppVersion "1.2.0" #ifndef MyWizardStyle #define MyWizardStyle "modern dynamic windows11" diff --git a/Installer/ThreadPilot.wxs b/Installer/ThreadPilot.wxs index c67b3c3..c924f7c 100644 --- a/Installer/ThreadPilot.wxs +++ b/Installer/ThreadPilot.wxs @@ -7,7 +7,7 @@ diff --git a/Installer/setup.iss b/Installer/setup.iss index 75ce6fe..9cfc206 100644 --- a/Installer/setup.iss +++ b/Installer/setup.iss @@ -11,7 +11,7 @@ #endif #ifndef MyAppVersion - #define MyAppVersion "1.1.3" + #define MyAppVersion "1.2.0" #endif #ifndef MyAppSourceDir diff --git a/MainWindow.Behaviors.partial.cs b/MainWindow.Behaviors.partial.cs index 828779d..a4da578 100644 --- a/MainWindow.Behaviors.partial.cs +++ b/MainWindow.Behaviors.partial.cs @@ -68,16 +68,21 @@ private void InitializeLoadingOverlay() // Ensure overlay is visible while initialization runs if (loadingOverlay != null) { - loadingOverlay.Visibility = Visibility.Visible; - loadingOverlay.Opacity = 1; + loadingOverlay.Visibility = this.isSilentStartupMode ? Visibility.Collapsed : Visibility.Visible; + loadingOverlay.Opacity = this.isSilentStartupMode ? 0 : 1; } - // Enable blur on main app content during startup loading. - this.ApplyUIContentBlur(15); + if (!this.isSilentStartupMode) + { + this.ApplyUIContentBlur(15); + } // Start spinner animation if available var spinnerAnimation = this.FindResource("SpinnerAnimation") as Storyboard; - spinnerAnimation?.Begin(); + if (!this.isSilentStartupMode) + { + spinnerAnimation?.Begin(); + } // Set a timeout guard for initialization this.initializationTimeoutTimer = new System.Timers.Timer(15000) @@ -115,6 +120,7 @@ private async Task InitializeApplicationAsync() await this.InitializeServicesAsync(); this.LogDebug("InitializeServicesAsync completed successfully"); this.CompleteInitializationTask("Services"); + this.QueueStartupUpdateCheck(); await this.Dispatcher.InvokeAsync(() => this.UpdateLoadingStatus("Finalizing startup...", "Applying final UI state and startup checks.")); this.LogDebug("Finalizing startup..."); @@ -133,6 +139,75 @@ private async Task InitializeApplicationAsync() } } + private void QueueStartupUpdateCheck() + { + if (Interlocked.Exchange(ref this.startupUpdateCheckStarted, 1) != 0) + { + return; + } + + TaskSafety.FireAndForget(this.CheckForUpdatesAtStartupAsync(), ex => + { + this.LogDebug($"Startup update check failed: {ex.Message}"); + }); + } + + private async Task CheckForUpdatesAtStartupAsync() + { + try + { + this.LogDebug("Startup update check started"); + var checker = this.serviceProvider.GetRequiredService(); + var currentVersion = GetCurrentApplicationVersion(); + var (latest, _) = await checker.GetLatestVersionAsync("PrimeBuild-pc", "ThreadPilot"); + + if (latest == null) + { + this.LogDebug("Startup update check completed without release information"); + return; + } + + if (latest <= currentVersion) + { + this.LogDebug($"Startup update check complete: installed {currentVersion}, latest {latest}"); + return; + } + + await this.notificationService.ShowNotificationAsync( + "Update available", + $"ThreadPilot {latest} is available from GitHub releases.", + NotificationType.Information); + this.LogDebug($"Startup update check found update: installed {currentVersion}, latest {latest}"); + } + catch (Exception ex) + { + this.LogDebug($"Startup update check ignored failure: {ex.Message}"); + } + } + + private static Version GetCurrentApplicationVersion() + { + var rawVersion = typeof(App).Assembly + .GetCustomAttributes(typeof(System.Reflection.AssemblyInformationalVersionAttribute), false) + .OfType() + .FirstOrDefault()? + .InformationalVersion + ?? typeof(App).Assembly.GetName().Version?.ToString() + ?? "0.0.0"; + + var sanitized = rawVersion.Trim(); + if (sanitized.StartsWith("v", StringComparison.OrdinalIgnoreCase)) + { + sanitized = sanitized[1..]; + } + + sanitized = sanitized.Split('-', '+')[0]; + + return Version.TryParse(sanitized, out var version) + ? version + : new Version(0, 0, 0); + } + private void UpdateLoadingStatus(string stage, string details = "") { if (this.mainWindowViewModel != null) @@ -160,6 +235,20 @@ private void HideLoadingOverlay() this.initializationTimeoutTimer?.Stop(); this.initializationTimeoutTimer?.Dispose(); + if (this.isSilentStartupMode) + { + var silentLoadingOverlay = this.FindName("LoadingOverlay") as Grid; + if (silentLoadingOverlay != null) + { + silentLoadingOverlay.Visibility = Visibility.Collapsed; + silentLoadingOverlay.Opacity = 0; + } + + this.ClearUIContentBlur(); + this.ApplyAppRefreshPolicy(AppActivityState.TrayHidden); + return; + } + // Stop spinner animation var spinnerAnimation = this.FindResource("SpinnerAnimation") as Storyboard; spinnerAnimation?.Stop(); @@ -186,6 +275,7 @@ private void HideLoadingOverlay() // Show elevation warning if needed this.TryShowElevationWarning(); + this.TryShowStartupMinimizedSuggestion(); }; fadeOutAnimation.Begin(); } @@ -204,6 +294,7 @@ private void HideLoadingOverlay() // Show elevation warning if needed this.TryShowElevationWarning(); + this.TryShowStartupMinimizedSuggestion(); } } catch (Exception ex) @@ -223,6 +314,7 @@ private void HideLoadingOverlay() // Show elevation warning if needed this.TryShowElevationWarning(); + this.TryShowStartupMinimizedSuggestion(); } catch (Exception fallbackEx) { @@ -1160,11 +1252,16 @@ private async Task StartProcessMonitoringManagerAsync() await startTask; // Get any exceptions this.LogDebug("Process monitoring manager service started, showing notification..."); - await this.notificationService.ShowSuccessNotificationAsync( - "ThreadPilot Started", - "Process monitoring and power plan management is now active"); + if (!this.isSilentStartupMode) + { + await this.notificationService.ShowSuccessNotificationAsync( + "ThreadPilot Started", + "Process monitoring and power plan management is now active"); + } - this.LogDebug("Success notification shown"); + this.LogDebug(this.isSilentStartupMode + ? "Startup success notification skipped for silent startup" + : "Success notification shown"); } catch (Exception ex) { @@ -1522,6 +1619,71 @@ private void TryShowPerformanceIntro() } } + private void TryShowStartupMinimizedSuggestion() + { + if (!this.showStartupMinimizedSuggestionOnReady + || this.isSilentStartupMode + || !this.isInitializationComplete + || this.isElevationWarningVisible) + { + return; + } + + try + { + if (!StartupMinimizedSuggestionPolicy.ShouldShow( + this.settingsService.Settings, + StartupWindowBehavior.Resolve(isAutostart: false, startMinimized: false))) + { + return; + } + + this.StartupMinimizedSuggestionOverlay.Visibility = Visibility.Visible; + } + catch (Exception ex) + { + this.LogDebug($"Failed to show startup minimized suggestion: {ex.Message}"); + } + } + + private async Task PersistStartupMinimizedSuggestionSeenAsync() + { + try + { + var settings = this.settingsService.Settings; + if (settings.HasSeenStartupMinimizedSuggestion) + { + return; + } + + settings.HasSeenStartupMinimizedSuggestion = true; + await this.settingsService.UpdateSettingsAsync(settings); + } + catch (Exception ex) + { + this.LogDebug($"Failed to persist startup minimized suggestion state: {ex.Message}"); + } + } + + private void HideStartupMinimizedSuggestion() + { + this.showStartupMinimizedSuggestionOnReady = false; + this.StartupMinimizedSuggestionOverlay.Visibility = Visibility.Collapsed; + } + + private async void StartupSuggestionOpenSettings_Click(object sender, RoutedEventArgs e) + { + await this.PersistStartupMinimizedSuggestionSeenAsync(); + this.HideStartupMinimizedSuggestion(); + this.SelectMainTab("Settings"); + } + + private async void StartupSuggestionDontShowAgain_Click(object sender, RoutedEventArgs e) + { + await this.PersistStartupMinimizedSuggestionSeenAsync(); + this.HideStartupMinimizedSuggestion(); + } + private void HidePerformanceIntro() { this.isPerformanceIntroVisible = false; @@ -1661,6 +1823,8 @@ private void HideElevationWarning() { elevationBlur.Radius = this.previousElevationBackdropBlurRadius; } + + this.TryShowStartupMinimizedSuggestion(); } private void ElevationWarningDismiss_Click(object sender, RoutedEventArgs e) diff --git a/MainWindow.xaml b/MainWindow.xaml index 6972afe..7a203ed 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -143,7 +143,7 @@ - + @@ -282,7 +282,7 @@ @@ -368,6 +368,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + diff --git a/app.manifest b/app.manifest index a6ccea0..970b893 100644 --- a/app.manifest +++ b/app.manifest @@ -1,6 +1,6 @@ - + diff --git a/build/build-installer.ps1 b/build/build-installer.ps1 index cb1d61f..5ef8e30 100644 --- a/build/build-installer.ps1 +++ b/build/build-installer.ps1 @@ -1,5 +1,5 @@ param( - [string]$Version = "1.1.3", + [string]$Version = "1.2.0", [string]$Configuration = "Release", [switch]$SkipPublish ) diff --git a/build/build-release.ps1 b/build/build-release.ps1 index 425f8b7..95dab48 100644 --- a/build/build-release.ps1 +++ b/build/build-release.ps1 @@ -1,5 +1,5 @@ param( - [string]$Version = "1.1.3", + [string]$Version = "1.2.0", [string]$Configuration = "Release", [string]$Runtime = "win-x64" ) diff --git a/build/package-release-zips.ps1 b/build/package-release-zips.ps1 index eb73351..b42c23a 100644 --- a/build/package-release-zips.ps1 +++ b/build/package-release-zips.ps1 @@ -1,5 +1,5 @@ param( - [string]$Version = "1.1.3" + [string]$Version = "1.2.0" ) $ErrorActionPreference = "Stop" diff --git a/chocolatey/threadpilot.nuspec b/chocolatey/threadpilot.nuspec index e6aca99..2cbcdc1 100644 --- a/chocolatey/threadpilot.nuspec +++ b/chocolatey/threadpilot.nuspec @@ -2,7 +2,7 @@ threadpilot - 1.1.3 + 1.2.0 ThreadPilot Prime Build https://github.com/PrimeBuild-pc/ThreadPilot @@ -15,7 +15,7 @@ false Advanced Windows process and power plan manager with rules automation and performance controls. ThreadPilot process and power plan manager. - https://github.com/PrimeBuild-pc/ThreadPilot/releases/tag/v1.1.3 + https://github.com/PrimeBuild-pc/ThreadPilot/releases/tag/v1.2.0 threadpilot process powerplan performance windows diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7ea5646..ce8218b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,44 @@ All notable changes to this project are documented in this file. +## v1.2.0 - CPU topology, persistent rules, and process control update + +### Added + +- CPU topology v2 support with `CpuSelection` for topology-aware affinity. +- Group-aware CPU Sets support and safer handling for processor groups and systems with more than 64 logical processors. +- Memory priority controls and persistent process rules. +- Apply at process start for saved rules while ThreadPilot is running. +- Process tab context menu actions, explicit Apply now, and Save as rule flows. +- Selected process summary panel for current affinity, priority, memory priority, and last operation status. +- Optional Diagnostics experience hidden by default. + +### Changed + +- README and release documentation now describe ThreadPilot as a process control center rather than a performance overlay. +- Default presets are gaming-oriented and topology-aware. +- Intel hybrid handling uses topology and `EfficiencyClass` instead of hardcoded SKU lists. +- AMD preset generation is CCD/L3-aware and avoids hardcoded SKU lists. +- Project version updated to 1.2.0. + +### Fixed + +- Startup binding crash caused by a display-only selected-process summary message binding to a read-only property with a TwoWay-capable target. +- CPU64 no longer aliases CPU0 in the new safe affinity paths. +- Persistent rule auto-apply cancellation is handled as shutdown/cancellation instead of logged as a warning. + +### Safety + +- CPU priority guardrails warn for High priority and block Realtime priority. +- Anti-cheat/protected-process failures use safe user messaging and ThreadPilot does not bypass protected processes. +- Persistent rules reuse the existing affinity, priority, memory-priority, and Realtime guardrail backend instead of duplicating apply logic. + +### Notes / limitations + +- Apply at process start is runtime-based and works only while ThreadPilot is running. +- No Windows Service, registry autorun, IFEO persistence, installer privilege workaround, tag, GitHub release, or generated release artifact is included in this update. +- Administrator rights can help normal access-denied cases but do not bypass protected-process or anti-cheat restrictions. + ## [1.1.6] - 2026-05-16 ### Added diff --git a/docs/release/RELEASE_NOTES.md b/docs/release/RELEASE_NOTES.md index 0f6c6e8..f434581 100644 --- a/docs/release/RELEASE_NOTES.md +++ b/docs/release/RELEASE_NOTES.md @@ -1,50 +1,57 @@ -# ThreadPilot v1.1.6 Release Notes +# ThreadPilot v1.2.0 Release Notes Draft ## Highlights -- Windows 11 native visual refresh across all major views with neutral Fluent surfaces. -- Refined sidebar navigation: separator lines softened for a cleaner, Settings-like appearance. -- Reduced visual weight and consistent card-based layouts in Rules, Logs, Performance, Settings, Tweaks, Process, Power Plans, and CPU Masks. -- Start minimized default clarified: `StartMinimized` now explicitly defaults to `false` for predictable manual-launch visibility. +- CPU topology v2 with topology-aware `CpuSelection`, CPU Sets, processor groups, and safer handling above 64 logical processors. +- New safe affinity paths where CPU64 no longer aliases CPU0. +- Intel hybrid handling through topology and `EfficiencyClass`, plus AMD CCD/L3-aware preset generation. +- Memory priority support and persistent process rules. +- Apply saved rules automatically when matching processes start while ThreadPilot is running. +- Process tab context menu actions, Save as rule, Apply now, and selected-process summary. ## Added -- Windows 11 visual refresh pass completed for neutral Fluent surfaces and card polish. -- Sidebar navigation separator polish: horizontal separator lines removed/softened for a native Windows 11 Settings-like feel. +- CPU topology v2 and `CpuSelection` for topology-aware affinity. +- Group-aware CPU Sets support and processor group safety. +- Memory priority controls. +- Persistent process rules with runtime apply-at-process-start support. +- Process tab context menu actions and selected-process summary. +- Optional Diagnostics view hidden by default. ## Changed -- `StartMinimized` defaults to `false` in `ApplicationSettingsModel`: manual exe launch opens the main window visibly by default. -- Older settings JSON without `startMinimized` field now reliably defaults to `false`. -- Explicit saved `startMinimized: true` or `startMinimized: false` values remain fully respected. -- Project version updated to 1.1.6. +- Default presets are gaming-oriented and generated from topology rather than hardcoded CPU SKU lists. +- Intel hybrid behavior uses Windows topology and `EfficiencyClass`. +- AMD behavior uses CCD/L3-aware preset generation. +- Project version updated to 1.2.0. ## Fixed -- Legacy settings without `startMinimized` no longer risk unexpected minimized startup. +- Startup no longer fails from a read-only selected-process summary binding. +- CPU64 no longer aliases CPU0 in new safe affinity paths. +- Persistent rule auto-apply cancellation does not log shutdown/future cancellation as a warning. -## Breaking Changes +## Safety -- None. +- High CPU priority shows a warning and Realtime priority remains blocked. +- ThreadPilot does not bypass anti-cheat or protected-process restrictions. +- Administrator rights may help ordinary access-denied cases but do not bypass protected processes. -## Installation +## Compatibility and Upgrade Notes -### Installer +- Requires Windows 11 build 22000 or newer. +- Existing legacy affinity profiles continue to load. +- New saved rules prefer topology-aware `CpuSelection` when safe topology mapping is available. +- Apply at process start works only while ThreadPilot is running. -1. Download ThreadPilot_v1.1.6_Setup.exe. -2. Run installer. -3. Launch ThreadPilot. +## Known Non-Goals -### Portable +- No anti-cheat bypass. +- No Windows Service. +- No registry or IFEO persistence. +- No generated release artifacts yet. +- No GitHub release or tag yet. -1. Download ThreadPilot_v1.1.6_singlefile_win-x64.zip. -2. Extract archive. -3. Run ThreadPilot.exe. +## Release Artifact Status -## Checksums - -See SHA256SUMS.txt in release assets. - -## Known Issues - -- Windows 10 support remains best effort. +- Installer, portable ZIP, checksums, package metadata verification, and release upload remain pending manual validation. diff --git a/docs/releases/v1.2.0-checklist.md b/docs/releases/v1.2.0-checklist.md new file mode 100644 index 0000000..cdaf394 --- /dev/null +++ b/docs/releases/v1.2.0-checklist.md @@ -0,0 +1,15 @@ +# ThreadPilot v1.2.0 Manual Validation Checklist + +- [ ] App starts without binding or startup errors. +- [ ] Process tab loads. +- [ ] Selected-process summary renders. +- [ ] Process tab context menu works. +- [ ] Affinity apply works on a normal single-group machine. +- [ ] CPU Sets clear works or fails safely with user-safe messaging. +- [ ] Memory priority set/read works or fails safely. +- [ ] Save as rule creates or updates a persistent process rule. +- [ ] Process-start auto-apply works while ThreadPilot is running. +- [ ] Protected or anti-cheat process operations fail safely without bypass language. +- [ ] Diagnostics is hidden by default. +- [ ] Startup does not enable performance monitoring or create performance-monitoring cost by default. +- [ ] Release artifacts are still pending manual validation. diff --git a/docs/releases/v1.2.0.md b/docs/releases/v1.2.0.md new file mode 100644 index 0000000..44e44a4 --- /dev/null +++ b/docs/releases/v1.2.0.md @@ -0,0 +1,44 @@ +# ThreadPilot v1.2.0 Release Notes Draft + +## Highlights + +- CPU topology v2: topology-aware `CpuSelection`, CPU Sets, processor groups, and safer handling above 64 logical processors. +- New safe affinity paths where CPU64 no longer aliases CPU0. +- Intel hybrid handling uses topology and `EfficiencyClass`; AMD preset generation is CCD/L3-aware. +- Memory priority support and persistent process rules. +- Saved rules can apply automatically when matching processes start while ThreadPilot is running. +- Process tab context menu actions, Save as rule, Apply now, and selected-process summary. +- Diagnostics remains optional and hidden by default. + +## Compatibility Notes + +- Requires Windows 11 build 22000 or newer. +- Administrator launch is still required for system-level process operations. +- Existing legacy affinity profiles remain supported. New safe paths prefer `CpuSelection` when topology data is available. +- CPU Sets and memory priority operations can fail on processes Windows denies access to; failures are reported safely. + +## Upgrade Notes for Legacy Profiles + +- Legacy affinity masks continue to load. +- When saving new rules from current Process tab settings, ThreadPilot attempts to store topology-aware `CpuSelection` when it can map the legacy selection safely. +- On systems where topology data is unavailable, ThreadPilot keeps the legacy affinity mask path instead of inventing an unsafe mapping. +- Users with old profiles should verify presets once on the target machine before relying on them for competitive or latency-sensitive workloads. + +## Limitations + +- Apply at process start is runtime-based and only works while ThreadPilot is running. +- ThreadPilot does not install a Windows Service for persistent rule application. +- ThreadPilot does not write registry or IFEO persistence for process rules. +- ThreadPilot does not bypass anti-cheat or protected-process restrictions. Administrator rights may help ordinary access-denied cases but do not override protected-process enforcement. + +## Known Non-Goals for This Draft + +- No anti-cheat bypass. +- No Windows Service. +- No registry or IFEO persistence. +- No generated release artifacts yet. +- No GitHub release or tag yet. + +## Release Artifact Status + +- Installer, portable ZIP, checksums, package metadata verification, and release upload remain pending manual validation. diff --git a/sonar-project.properties b/sonar-project.properties index 6613df3..c9fb7ea 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.projectKey=threadpilot sonar.projectName=ThreadPilot -sonar.projectVersion=1.1.3 +sonar.projectVersion=1.2.0 sonar.sourceEncoding=UTF-8 sonar.sources=.