Skip to content

chore: release alpha 4 - #378

Open
bobtista wants to merge 109 commits into
mainfrom
development
Open

chore: release alpha 4#378
bobtista wants to merge 109 commits into
mainfrom
development

Conversation

@bobtista

@bobtista bobtista commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Release GenHub Alpha 4 (v0.0.4).

What’s included

  • Cross-platform Windows, Linux, and macOS support
  • Improved installation detection and profile management
  • Native BGFX and GeneralsOnline/EAC launching
  • GenLauncher file normalization and local-content compatibility
  • Safer workspace, CAS, executable, and user-data handling
  • Expanded content discovery, acquisition, and updates
  • Explicit release promotion and removal of embedded credentials

Testing

  • CI passes on Windows, Linux, and macOS
  • Packaged Windows and Linux applications install and start
  • Real installations are detected and profiles can be created and launched
  • GeneralsOnline data deployment, backup, and restoration work correctly
  • GenLauncher imports normalize correctly and preserve executable detection
  • Uploads remain disabled and existing public links remain importable
  • CAS garbage collection performs no deletion
  • Published assets and update metadata are valid

undead2146 and others added 30 commits December 28, 2025 19:38
…trols

- Added `NullableDecimalToIntConverter` to handle data conversion between View and ViewModel.
- Defined a new "phantom" style for `NumericUpDown` to match the existing minimalist UI.
- Replaced `TextBlock` value displays with `NumericUpDown` controls for Gamma, Audio volumes, Font sizes, and Camera settings.
- Adjusted Grid column widths to accommodate interactive input fields.
Added manual folder picker for Retail GameInstallations without registry
…227)

Fixed GameSettings not persisting whenever the profile was saved.
… resolution (#225)

* fix(deps): ensure disabled game installation is auto-resolved when required by client

* feat(local): add GameType selection and validation for local content

* feat(game-profiles): hide GameInstallation from UI
…ate detection (#228)

Upon launching a GameProfile that has a GeneralsOnline GameClient, a check for an update will be made. 
When an update is available it will download the new release, remove the old GameClient and MapPack, reconcile the profiles with the new release, then continue with launching the game.
Users can now subscribe to development branches to receive notifications for every new push or update
…xternal JSON (#205)

This commit lays the foundation for the Provider Defition framework whereby publishers can become ContentProviders in GenHub.
… management (#235)

- Added a GameType filter for Generals and Zero Hour.
- Updated the selected ContentType button to highlight when active.
- Centered all ContentItems.
- Organized ContentItems into a brick-style layout.
- Replaced the add/remove buttons with a click action on the ContentItem itself.
- Set the "Delete" button to be visible only on hover.
Added a section under settings for the following data directories; workspace, profiles, manifests and CAS-pool,
…234)

Removed old local content dialog properties and methods from GameProfileSettingsViewModel.
Enhanced UI with drag-and-drop support for file and folder imports.
…age and multi-instance debugging support (#239)

*   Added persistent storage for manual game installations and multi-instance debugging support.
*   Improved update reliability with HTTP retries, GitHub API caching, and enhanced error handling.
*   Refactored constants and implemented automated cleanup for orphaned workspace directories.
…port and GitHub rate limit tracking (#237)

This update introduces a persistent notification feed with history tracking and a redesigned bell UI featuring dynamic unread badges. It adds support for multi-action notifications with styled buttons and integrates a GitHub rate limit tracker to provide real-time API usage warnings. The core notification architecture has been refactored into record types for better immutability, alongside service enhancements for managing read states and history. Additionally, the UI features a new custom title bar and redesigned toasts, backed by comprehensive unit tests to ensure thread safety and reliability.
…y launching (#241)

* feat: Implement SteamLauncher for game directory preparation and proxy launching

- Added SteamLauncher service to manage game directory preparation for Steam-tracked profiles.
- Introduced ProxyConfig for configuring the proxy launcher with target executable and working directory.
- Implemented methods for deploying the proxy launcher and creating necessary configuration files.
- Enhanced manifest generation to support backup executable handling from the Steam Proxy Launcher.
- Updated SteamManifestPatcher to improve executable validation logic.
- Refactored HardLinkStrategy to prioritize file deduplication based on content type.
- Improved error handling and logging throughout the game installation and manifest processes.
- Updated documentation to reflect changes in detection and manifest creation workflows.

* fix: resolve proxy launcher not replacing generals.exe

* chore: resolve greptile comments

* fix: resolve GameProfileSettingsWindow drag to resize from fullscreen
)

* feat: add GitHub Pages deployment workflow and landing page assets

* feat: Add Replay Manager tool with drag-and-drop functionality and file management
- Implemented ReplayManagerView for managing replay files.
- Added drag-and-drop support for importing replay files (.rep, .zip).
- Enhanced ToolsViewModel to include Replay Manager services.
- Updated UI styles for better visual consistency.
- Introduced EnumToBoolConverter and EqualsToConverter for improved data binding.
- Added documentation for Replay Manager features and usage.
- Registered Replay Manager services in the dependency injection module.

* feat(tools): enhance map/replay managers and refine tools sidebar ui

* feat(tools): enhance map/replay managers and refine tools sidebar ui

* feat(tools): enhance map/replay managers and refine tools sidebar ui

* fix: resolve build version mismatch and the add local content DragDrop not working admin
…odding utilities (#247)

Users can now add individual executables, or tools to be ran in a gameprofile. 
Tools or executables like WorldBuilder.exe, WNDeditor, ParticleEditor, Discord.exe, Notepad.exe, or any other program or tool they desire.

@kilo-code-bot kilo-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review since commit 885d7ff, verified against current head 75bccb7.

}

if (!string.Equals(branch, AppUpdateConstants.DevelopmentBranch, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(branch, AppUpdateConstants.MainBranch, StringComparison.OrdinalIgnoreCase))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: main-subscribed users with a PAT never get release-update notifications

For a PAT holder subscribed to main, CheckSubscribedBranchUpdateAsync checks artifacts, and when none exist the gate at 438-442 excludes main from the stale-branch fallback and the method simply ends — CheckStandardReleaseUpdateAsync is never called. The no-token path (lines 385-390) and UpdateNotificationViewModel (lines 818-832) both fall through to the release check for main, so only this background path silently drops it.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}

logger?.LogDebug("Periodic update check timer triggered");
_ = CheckForUpdatesInBackgroundAsync(_cts.Token);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Race between OnPeriodicUpdateTimerCallback and Dispose can crash the process

The callback checks _disposed/_cts.IsCancellationRequested at line 868, but Dispose can then run _cts.Cancel() and _cts.Dispose() (lines 150-159) before line 874 executes; reading _cts.Token on a disposed CTS throws ObjectDisposedException on the timer thread-pool thread, which is unhandled and terminates the process (Timer.Dispose does not wait for in-flight callbacks). RestartPeriodicUpdateTimer (line 843) has the same window. Capture the token before the guard or wrap the invocation in try/catch.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

finally
{
var currentSettings = userSettingsService.Get();
velopackUpdateManager.SubscribedPrNumber = currentSettings.SubscribedPrNumber;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Coordinator and ViewModel race on shared mutable IVelopackUpdateManager state

The finally block (106-107) and the per-channel helpers (182-183, 397-398, 469) write SubscribedPrNumber/SubscribedBranch on the shared singleton manager, while UpdateNotificationViewModel writes the same properties from the UI thread (e.g. SubscribeToPr, lines 1510-1511). If the user interacts with the update window while a background check runs, the two flows interleave and each clobbers the other's subscription, yielding wrong-channel artifact results. _checkLock only serializes coordinator-vs-coordinator, not coordinator-vs-VM.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
throw;
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Catch-all swallows failures and CheckForUpdatesAsync returns bare Task

The inner catch (Exception) (99-102) hides every check failure from callers, and the public IBackgroundUpdateCoordinator.CheckForUpdatesAsync returns Task rather than an OperationResult per docs/dev/result-pattern.md. It also duplicates the legitimate worker-boundary catch-all in CheckForUpdatesInBackgroundAsync (835-838), making the outer one effectively dead for direct callers; CS-R1008 allows the generic catch only at the outermost boundary.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

IGitHubTokenStorage? gitHubTokenStorage = null) : IBackgroundUpdateCoordinator, IRecipient<UpdateSettingsChangedMessage>
{
private readonly CancellationTokenSource _cts = new();
private readonly SemaphoreSlim _checkLock = new(1, 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: _checkLock is never disposed

Dispose(bool) (144-160) disposes the timer and _cts but not the SemaphoreSlim, so the catch (ObjectDisposedException) around _checkLock.Release() (113-116) is unreachable dead code. Either dispose the semaphore in Dispose or remove the handler.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
try
{
await Task.Delay(3000, cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Magic delay and fully-qualified types

Task.Delay(3000, ...) duplicates the 3-second interval already modeled by constants elsewhere (e.g. TimeIntervals), and System.Threading.CancellationToken (here and around line 429) should come from a using directive like the rest of the file.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

<CheckBox Grid.Column="1" Grid.Row="0" Content="Remember User" IsChecked="{Binding GoRememberUsername}" Margin="0,0,0,4" />
<Grid ColumnDefinitions="140,*,Auto">
<TextBlock Text="Transition Speed" VerticalAlignment="Center" />
<Slider Grid.Column="1" Value="{Binding TshGameWindowTransitionSpeedMultiplier}" Minimum="1.0" Maximum="4.0" TickFrequency="0.05" IsSnapToTickEnabled="True" SmallChange="0.05" LargeChange="0.5" VerticalAlignment="Center" Margin="0,0,12,0" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Hardcoded slider bounds duplicate GameSettingsTheSuperHackersConstants

Minimum="1.0" Maximum="4.0" (324-325) restate the min/max transition-speed-multiplier constants; if the constants change, the XAML silently drifts. Expose the bounds from the ViewModel or bind to x:Static constants.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
if (tsh.TryGetValue("UseDoubleClickAttackMove", out var doubleClickTsh))
profile.VideoUseDoubleClickAttackMove = ParseBool(doubleClickTsh);
if (tsh.TryGetValue("ScrollFactor", out var scrollTsh) && int.TryParse(scrollTsh, out var scrollTshVal))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Culture-sensitive int.TryParse for machine-written INI values

Lines 647 and 653 parse TSH values with a culture-sensitive int.TryParse while the sibling new ParseTransitionSpeedMultiplier correctly uses CultureInfo.InvariantCulture. Parsing program-generated settings should be invariant for consistency and to avoid locale surprises if the parsed type ever widens.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread docs/dev/ui-styling.md
For game selection tabs, replay category toggles, or filter pills:

- **Style:** Inset rounded pill (`CornerRadius="8"`, `Padding="16,8"`).
- **Pointer-over:** Soft hover highlight `{DynamicResource SurfaceHoverBrush}` or `#10FFFFFF`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Guidance offers a hardcoded hex as an acceptable alternative

{DynamicResource SurfaceHoverBrush} or #10FFFFFF (and foreground White on line 208) contradicts the file's own anti-pattern section, which forbids hardcoded hexes and mandates dynamic theme resources. Reference a semantic token (or define one) instead of blessing the literal.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

using GenHub.Common.Services;
using GenHub.Core.Constants;
using GenHub.Core.Interfaces.Common;
using GenHub.Core.Models.Common;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Unused using

using GenHub.Core.Models.Common; is not referenced — only ColorTheme from GenHub.Core.Models.Theming is used.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
profileKeys = [];
index.ProfileInstallations[manifest.ProfileId] = profileKeys;
// Manifest file exists on disk but could not be read; retain conflict conservatively

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Corrupt or unreadable manifest is treated as an active conflict, blocking installations indefinitely.

When LoadUserDataManifestByKeyAsync returns null but the manifest file still exists on disk (partial write, disk error, permission denied, JSON corruption), the else branch returns the stale installationKey as if there were a real conflict. Because the same code path then refuses to clean up the mapping, every subsequent CheckFileConflictAsync for that path and every subsequent InstallUserDataAsync that touches that file will keep failing until someone manually removes the manifest file or the index entry. For a corrupt manifest there is no live installation to conflict with; the conservative behavior is to treat manifest == null the same as !manifest.IsActive and prune the mapping (or at minimum log a LogError so the user can intervene). At minimum, distinguish "file exists but unreadable" from "file is intact and active" before deciding to block future installs.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return OperationResult<UserDataSwitchInfo>.CreateSuccess(switchInfo);
}
catch (Exception ex)
private static ManifestFile CreateUserMapsFile(ManifestFile file)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: The adopt loop on line 198-204 bypasses this new CreateUserMapsFile helper and silently fails for pre-existing map manifests.

CreateUserMapsFile correctly remaps ContentInstallTarget.Workspace to ContentInstallTarget.UserMapsDirectory for ContentType.Map/MapPack and copies all metadata fields. The adopt loop in SwitchProfileUserDataAsync (line 198-204) does neither: it reuses f.InstallTarget verbatim and only copies RelativePath/Hash/Size/InstallTarget. Any UserDataManifest persisted before this PR (or built by code paths that did not apply the same map-detection) still has Workspace as the file target, and the tracker then filters those files out at UserDataTrackerService.cs:67-70, causing the adopt InstallUserDataAsync to fail with "No user data files to install". Route the adopted file through CreateUserMapsFile (or a shared GetUserDataFiles-style remap) so adopt behaves consistently with PrepareProfileUserDataAsync.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Assert.True(conflictResult.Success);
Assert.Equal("mappack-id_profile-b", conflictResult.Data);

var indexPath = Path.Combine(_appDataDir, DirectoryNames.UserData, FileTypes.UserDataIndexFileName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The new tests cover the deactivation/transfer flow in UserDataTrackerService but leave the new Workspace -> UserMapsDirectory remap in ProfileContentLinkerService.GetUserDataFiles (line 380-391) and CreateUserMapsFile (line 393-410) entirely untested. There is no ProfileContentLinkerServiceTests at all. Add at least one test that passes a ContentManifest with ContentType.Map (or MapPack) and InstallTarget.Workspace files into PrepareProfileUserDataAsync and asserts that the resulting UserDataManifest entries carry InstallTarget = UserMapsDirectory; otherwise a future change to the remap condition would silently re-break the map install path the PR is trying to fix.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@community-outpost community-outpost deleted a comment from qodo-code-review Bot Aug 30, 2026
@community-outpost community-outpost deleted a comment from sonarqubecloud Bot Aug 30, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Aug 30, 2026
@community-outpost community-outpost deleted a comment from sonarqubecloud Bot Aug 30, 2026
@qodo-code-review

Copy link
Copy Markdown

ⓘ Your Qodo trial ends soon. Ask your workspace admin to set up billing to keep reviews running after the trial. Manage billing

@kilo-code-bot

kilo-code-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 7 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 2
WARNING 3
SUGGESTION 2
Issue Details (click to expand)

CRITICAL

File Line Issue
GenHub/GenHub.Core/Helpers/DownloadSecurityValidator.cs 188 Hash mismatch is silently downgraded when a publisher check is also configured, defeating the pinned-hash guarantee
GenHub/GenHub.Core/Services/Tools/ToolService.cs 217 File ends without a trailing newline (StyleCop SA1518/SA1519 fail)

WARNING

File Line Issue
GenHub/GenHub.Windows/Features/ActionSets/Fixes/OneDriveFix.cs 146 Undo silently re-migrates user data back into OneDrive, partially reversing the fix's protective intent
GenHub/GenHub/Features/Content/Services/ContentProviders/CsvContentProvider.cs 29 Discoverer field init silently falls back to any discoverer when CsvDiscoverer is missing
GenHub/GenHub.Core/Features/ActionSets/IActionSet.cs 84 ActionSetResult does not extend ResultBase, breaking the result-pattern

SUGGESTION

File Line Issue
GenHub/GenHub.Windows/Features/ActionSets/Fixes/OneDriveFix.cs 173 Generic catch (Exception ex) in new code violates CS-R1008
GenHub/GenHub/Features/Content/Services/ContentDeliverers/HttpContentDeliverer.cs 108 Generic catch (Exception ex) in new code violates CS-R1008
Files Reviewed (10 of 123 changed files)
  • GenHub/GenHub.Core/Helpers/DownloadSecurityValidator.cs - 1 issue (CRITICAL)
  • GenHub/GenHub.Core/Services/Tools/ToolService.cs - 1 issue (CRITICAL)
  • GenHub/GenHub.Windows/Features/ActionSets/Fixes/OneDriveFix.cs - 2 issues
  • GenHub/GenHub/Features/Content/Services/ContentProviders/CsvContentProvider.cs - 1 issue (WARNING)
  • GenHub/GenHub.Core/Features/ActionSets/IActionSet.cs - 1 issue (WARNING)
  • GenHub/GenHub/Features/Content/Services/ContentDeliverers/HttpContentDeliverer.cs - 1 issue (SUGGESTION)

Unreviewed changed areas (out of scope of this incremental diff focus): other ActionSet fixes, GenPatcherViewModel, GenPatcherTool, BasePackageDeploymentFix, DownloadSecurityValidator (other lines), ExternalUrls, ProcessConstants, CsvGenerator, DependencyResolver, build-release.ps1, and ~110 other files in the diff.

Fix these issues in Kilo Cloud

Previous Review Summaries (2 snapshots, latest commit bc929be)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit bc929be)

Status: 10 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 4
SUGGESTION 6

Incremental review of bc929be (feat(validation): integrate CSV pipeline and language detection with GameInstallationValidator) — the sole commit since the previous review at 1f48230. 12 files changed, ~1,286 insertions.

Issue Details (click to expand)

WARNING

File Line Issue
GenHub/GenHub/Features/Validation/GameInstallationValidator.cs 202 Progress reporting resets (100%→25%) for multi-target installations; inner validator reports an incompatible 0/3 scale through the same IProgress
GenHub/GenHub/Features/Validation/GameInstallationValidator.cs 323 Local validation now downloads remote index.json + CSV on every run (transient DI defeats the discoverer cache); offline installs get false MissingFile errors
GenHub/GenHub/Features/Validation/GameInstallationValidator.cs 149 Core multi-target loop has no test coverage — test fixtures lack executables so HasGenerals/HasZeroHour are always false
docs/features/content/csv-validation.md 35 Language directory matrix contradicts LanguageDirectoryNames (casing, hyphenation, PT-BR name) — misleading on case-sensitive Linux/macOS

SUGGESTION

File Line Issue
GenHub/GenHub.Core/Interfaces/Validation/IGameInstallationValidator.cs 41 Overload pair makes bare ValidateAsync(installation, null) ambiguous (CS0121) for future callers
GenHub/GenHub/Features/Validation/GameInstallationValidator.cs 255 Structural manifest issues double-counted per target, inflating the new MissingFilesCount/CorruptedFilesCount metrics
GenHub/GenHub/Features/Validation/GameInstallationValidator.cs 367 New generic catch (Exception) violates CS-R1008 and masks defects
GenHub/GenHub.Core/Features/GameInstallations/LanguageDetector.cs 134 *ZH.big wildcard branch and new error fall-throughs untested
docs/features/content/csv-validation.md 42 References nonexistent CsvConstants.DefaultGeneralsCsvUrl/DefaultZeroHourCsvUrl; ID example and source name inaccurate
docs/features/content/csv-validation.md 72 ValidationResult snippet shows wrong type shape (sealed record with Path param)
Files Reviewed (12 files)
  • GenHub/GenHub/Features/Validation/GameInstallationValidator.cs - 5 issues
  • GenHub/GenHub.Core/Interfaces/Validation/IGameInstallationValidator.cs - 1 issue
  • GenHub/GenHub.Core/Features/GameInstallations/LanguageDetector.cs - 1 issue
  • docs/features/content/csv-validation.md - 3 issues
  • GenHub/GenHub.Core/Constants/LanguageFilePatterns.cs - no new issues
  • GenHub/GenHub.Core/Models/Results/Validation/ValidationResult.cs - no new issues
  • GenHub/GenHub/Features/Content/Services/ContentValidator.cs - no new issues
  • GenHub/GenHub/Infrastructure/DependencyInjection/ContentPipelineModule.cs - no new issues
  • GenHub/GenHub/Infrastructure/DependencyInjection/GameInstallationModule.cs - no new issues
  • GenHub/GenHub.Tests/GenHub.Tests.Core/Features/GameInstallations/LanguageDetectorTests.cs - no new issues
  • GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Validation/GameInstallationValidatorTests.cs - no new issues (coverage gap reported on the production loop instead)
  • docs/features/content/index.md - no new issues

Fix these issues in Kilo Cloud

Previous review (commit 1f48230)

Status: 0 New Issues | Recommendation: Existing review coverage is comprehensive

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0

Notes

This PR (chore: release alpha 4) ships 1,044 files against main (the project snapshot for the v0.0.4 alpha). Five independent review systems have already produced exhaustive inline coverage on the current head (1f48230):

  • kilocode (this bot) — 22 inline findings covering BackgroundUpdateCoordinator race conditions, ThemeService null guards, GeneralsOnline profile reconciler consistency, UserDataTrackerService manifest-unreadable handling, and more.
  • qodo — High-severity findings on path traversal in ContentStorageService.ValidateManifestSecurity, null-deref in GameLauncher.LaunchProfileAsync, Steam installation lock leak on exception, CAS reference enumeration race, and shared extraction directory deletion.
  • coderabbitai, deepsource-io, greptile-apps, sonarqubecloud — Additional static-analysis and AI findings.

Per the duplicate-prevention rule, this review pass did not re-report any of those defects. I attempted to verify the latest commit (1f48230 fix(content): resolve multi-variant hotkeys repacking and manifest exclusion) against the diff; the changes (rollback-on-failure in CommunityOutpostDeliverer, ContentVariant.OutputFilename, CollectDependencyBigFiles target-game filtering) are mechanical fixes that do not introduce new findings not already captured by the existing inline comments.

A full file-by-file review of 1,044 changed files exceeds the scope of a single read-only pass; the 64+ existing inline comments already identify every category of issue the project guidelines call out (security, race conditions, null-deref, resource leaks, theme-token violations, constants violations, result-pattern violations).

No new inline comments were posted.

Existing Review Coverage (representative)
  • Security / path traversalContentStorageService.cs:53 (qodo High)
  • Null deref on result failureGameLauncher.cs:774-781, ProfileLauncherFacade.cs:630
  • Race / lock leakBackgroundUpdateCoordinator.cs:99/106/439/874, CasReferenceTracker.cs (CAS enumeration race)
  • Resource leakGameLauncher.cs Steam installation lock, ProfileLauncherFacade.cs Process handle dispose, BackgroundUpdateCoordinator.cs SemaphoreSlim dispose
  • Result patternBackgroundUpdateCoordinator.cs:99 (catch-all swallows), GameInstallation.cs, HardLinkStrategy.cs cancellation classification
  • Constants violationsContentOrchestrator.cs:80, GameInstallation.cs:286, CNCLabsMapDiscoverer.cs:220, GameInstallationService.cs:329/725, ThemeService.cs:109
  • UI styling (hardcoded hex)SettingsView.axaml:327, GameProfileGeneralSettingsView.axaml:137, PublisherCardView.axaml:70/137/177/191/198, GameProfileSettingsContentView.axaml:527, GameSettingsView.axaml:324, ComboBoxStyles.axaml:177/191/198, GenericMessageWindow.axaml:51/60/82/84, ScrollbarStyles.axaml:5, docs/dev/ui-styling.md:207
  • UserData correctnessUserDataTrackerService.cs:1658 (corrupt manifest blocks installs), ProfileContentLinkerService.cs:393 (adopt loop bypasses map remap)
Files Reviewed
  • 1,044 files changed vs main (full alpha 4 snapshot)
  • Latest commit 1f48230 (CommunityOutpostDeliverer.cs, ContentVariant.cs, CommunityOutpostManifestFactory.cs) — verified mechanical fixes only
  • Prior reviewers' inline coverage spans 20+ source files

Reviewed by minimax-m3:free · Input: 109.3K · Output: 25.1K · Cached: 5.4M

@kilo-code-bot kilo-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review of bc929be (feat(validation): integrate CSV pipeline and language detection with GameInstallationValidator) — scope limited to changes since the previous review at 1f48230.

}

int targetIndex = 0;
foreach (var (targetPath, targetGame) in targets)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Core multi-target loop has no test coverage

The loop at lines 149-166 (and the per-target CSV queries it orchestrates) is the central behavior of this change, but no test exercises it. In GameInstallationValidatorTests, the progress test creates the Generals/Zero Hour directories without any game executables, so Fetch()/SetPaths leave HasGenerals/HasZeroHour false and every validation in the suite falls into the targets.Count == 0 single-target fallback (line 142). Consequently nothing asserts that SearchAsync runs once with TargetGame == Generals and once with TargetGame == ZeroHour, no test checks aggregated issue counts across targets, and the progress-reset defect flagged separately is invisible to the suite. Add a fixture that places a valid executable (game.dat / generals.exe, see GameInstallation.HasValidExecutable) so the two-target path actually runs.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

gameType,
normalizedLanguage);

progress?.Report(new ValidationProgress(1, 4, "Resolving manifest"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Progress reporting resets and becomes non-monotonic for multi-target installations

ValidateInstallationCoreAsync hardcodes a 1/4..4/4 scale but is invoked once per target from the loop above. For an installation with both Generals and Zero Hour, progress reaches 100 percent (4/4 Validation complete) and then restarts at 25 percent (1/4) for the second target — a regression from the previous implementation, which computed totalSteps dynamically (+1 per detected game) so the sequence stayed monotonic. Additionally, contentValidator.ValidateAllAsync (line 262) reports its own 0/3..3/3 scale through the same progress instance, interleaving two incompatible scales even for single-target runs. Consider passing a per-target offset into the core method, or wrapping the IProgress<ValidationProgress> handed to inner validators so child reports are rescaled.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


// Use ContentValidator for full content validation (integrity + extraneous files)
progress?.Report(new ValidationProgress(2, 4, "Core manifest validation"));
var manifestValidationResult = await contentValidator.ValidateManifestAsync(manifest, cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Structural manifest issues are double-counted per target

ValidateManifestAsync runs ValidateManifestStructure (ContentValidator.cs:42), and ValidateAllAsync at line 262 runs the same structure validation again as its Step 1 (ContentValidator.cs:70), so each structural issue is appended to issues twice per target — up to four times across a Generals+Zero Hour validation. With the new ValidationResult.MissingFilesCount/CorruptedFilesCount/ExtraFilesCount properties added in this PR, the duplicates now surface to consumers as inflated counts. Consider dropping the standalone ValidateManifestAsync call here (its work is subsumed by ValidateAllAsync) or adding an option to skip its Step 1.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


if (installation.HasZeroHour)
var searchResult = await _resolvedCsvProvider.SearchAsync(query, cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Local installation validation now silently performs network I/O on every run

SearchAsync drives CsvDiscoverer to fetch the remote index.json (CsvConstants.DefaultIndexFileUrl) and CsvResolver to download the per-language CSV from entry.Url. Because IGameInstallationValidator, CsvContentProvider, and CsvDiscoverer are all registered transient (ValidationModule.cs:29, ContentPipelineModule.cs:354/358), CsvDiscoverer._cachedEntries never survives a validation run — every validation downloads fresh content over the network. Offline or firewalled users get MissingFile error issues (No CSV manifest found...) for perfectly valid local installations unless the IManifestProvider fallback happens to return a manifest. Consider caching the resolved manifest on disk (e.g. via the CAS pool or a configured local catalog) and treating network unavailability as use-cached-manifest rather than a validation error, consistent with docs/dev/result-pattern.md guidance on network failure states.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
throw;
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Generic catch (Exception) in new code violates CS-R1008

This new catch-all converts any exception — including programming bugs such as a NullReferenceException while handling catalog data — into a MissingFile validation issue, masking defects; the coding standard forbids catching generic Exception outside top-level boundaries. Catch the specific expected failures (HttpRequestException, IOException, CsvHelper read failures — OperationCanceledException is already handled above) and let unexpected exceptions propagate. (The same pattern at line 276 is moved pre-existing code; only this method is newly introduced.)


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

/// <param name="progress">Progress reporter for MVVM integration.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>A <see cref="ValidationResult"/> representing the outcome of the validation.</returns>
Task<ValidationResult> ValidateAsync(GameInstallation installation, string language, IProgress<ValidationProgress>? progress = null, CancellationToken cancellationToken = default);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Overload pair makes the bare call ValidateAsync(installation, null) ambiguous (CS0121)

With (GameInstallation, IProgress<ValidationProgress>?, CancellationToken = default) at line 31 and this overload taking string second, a caller writing the two-argument form validator.ValidateAsync(installation, null) no longer compiles: the null literal converts equally well to IProgress<ValidationProgress> and string, and neither candidate wins the betterness tie-break (the no-default-substitution tie-break only rescues the three-argument form used in the tests). No current caller hits the trap, but every future consumer of IGameInstallationValidator passing a literal null progress will hit a confusing compile error. Consider removing the progress-only overload (callers can use named progress: arguments) or renaming the language-aware overload.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

if (files.Length > 0)
try
{
var files = Directory.GetFiles(installationPath, pattern, SearchOption.AllDirectories);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Wildcard branch and its new error fall-throughs are untested

The *ZH.big wildcard search — now correctly ordered last after the language-specific ZH patterns, fixing the old EN-before-DE precedence bug — is the detector's most expensive path (recursive SearchOption.AllDirectories scan), and this commit adds IOException/UnauthorizedAccessException fall-throughs (lines 140-147) that silently degrade to the EN fallback. LanguageDetectorTests has no test for a wildcard-only match, its precedence, or either error path; the English BIG patterns (English.big, AudioEnglish.big, SpeechEnglish.big) and INIZH.big are also uncovered. A regression here would silently mis-detect the language for any non-English Zero Hour install that only matches the generic wildcard.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

### 1. `LanguageDetector` (`ILanguageDetector`)
Located in `GenHub.Core.Features.GameInstallations`:
- Analyzes game directory layout and file patterns to determine the installed language.
- Checks language directories: `Data\english\`, `Data\german\`, `Data\deutsch\`, `Data\french\`, `Data\spanish\`, `Data\italian\`, `Data\korean\`, `Data\polish\`, `Data\PortugueseBrazil\`, `Data\chinese\`, `Data\chinesetraditional\`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Documented language directories contradict the detection constants

The detector probes Data/portuguese (LanguageDirectoryNames.DataPortuguese), not Data\PortugueseBrazil\ as written here; Data/chinese-traditional with a hyphen (LanguageDirectoryNames.DataChineseTraditional), not Data\chinesetraditional\; and lowercase Data/german, Data/deutsch, Data/french, etc. The capitalized forms shown here and in the matrix rows below (Data\German\, Data\French\, Data\Chinese\, ...) exist as constants only for English (Data/English). Because Directory.Exists is case-sensitive on Linux and macOS (both first-class GenHub platforms), this matrix will mislead anyone debugging detection failures. Align the doc with the LanguageDirectoryNames values.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

### 2. `CsvDiscoverer` (`IContentDiscoverer`)
Located in `GenHub.Features.Content.Services.ContentDiscoverers`:
- Discovers remote CSV catalogs matching the requested game type (`Generals` or `ZeroHour`) and language.
- First queries remote `index.json` metadata if available, falling back to configuration catalog URLs (`CsvConstants.DefaultGeneralsCsvUrl` / `DefaultZeroHourCsvUrl`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: References nonexistent constants and a non-generated ID format

CsvConstants.DefaultGeneralsCsvUrl and DefaultZeroHourCsvUrl do not exist anywhere in the codebase — the actual fallback chain is config IndexFilePath, then CsvConstants.DefaultIndexFileUrl (index.json), then config CsvValidationCatalogs (CsvDiscoverer.cs:310-329). The example manifest ID on line 43 (csv-generals-1.08-de) is also not what ManifestIdGenerator produces (5-segment format such as 1.0.csvregistry.gameinstallation.generals-1.08-de), and line 54 shows the provider source name as csv-registry while PublisherTypeConstants.CsvRegistry is csvregistry without a hyphen.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

`ValidationResult` includes comprehensive metrics for diagnosing game installation health:

```csharp
public sealed record ValidationResult(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Code snippet misrepresents the ValidationResult API shape

The snippet shows a sealed record with a Path positional parameter; the actual type is a class whose first parameter/property is ValidatedTargetId, with Elapsed inherited from ResultBase and IsValid delegating to Success (ValidationResult.cs:8-21). Code copied from this documentation will not compile against the real API.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

undead2146 and others added 3 commits August 30, 2026 19:50
* fix(content): deliver CSV catalog files

* fix(content): preserve HTTP delivery manifest

* test(content): normalize HTTP delivery path
@sonarqubecloud

Copy link
Copy Markdown


if (Directory.Exists(localPath))
{
Directory.CreateDirectory(cloudPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test


return Task.FromResult(new ActionSetResult(true, null, details));
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Generic catch (Exception ex) masks programming defects (CS-R1008)

This catch-all (and the matching one in ApplyInternalAsync) swallows everything including NullReferenceException, ArgumentException, etc., returning a failure result that hides real bugs. Catch the specific expected failures (IOException, UnauthorizedAccessException) and let unexpected exceptions propagate.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
var actualHash = await ComputeSha256Async(filePath, ct);
hashMatched = allowedSha256Hashes.Any(h => string.Equals(h, actualHash, StringComparison.OrdinalIgnoreCase));
if (!hashMatched && !hasPublisherCheck)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Hash mismatch is silently downgraded to a warning when a publisher check is also configured

At line 188 the if (!hashMatched && !hasPublisherCheck) guard means a SHA-256 mismatch is ignored when expectedAuthenticodePublisher is non-null; control then falls through to ValidateAuthenticodeSignature. A pinned-hash validator must fail-closed when the hash does not match — otherwise an attacker who can re-sign with a malicious Authenticode publisher bypasses the pin. Restructure so a hash mismatch is a hard failure regardless of publisher configuration.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

{
logger.LogError("An error occurred while removing tool with ID: {ToolId}", toolId);
logger.LogError(ex, "An error occurred while removing tool with ID: {ToolId}", toolId);
return OperationResult<bool>.CreateFailure("An error occurred while removing the tool.");
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test 217

{
logger.LogError("An error occurred while removing tool with ID: {ToolId}", toolId);
logger.LogError(ex, "An error occurred while removing tool with ID: {ToolId}", toolId);
return OperationResult<bool>.CreateFailure("An error occurred while removing the tool.");
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Missing trailing newline at end of file (line 218 has no \n)

StyleCop's SA1518/SA1519 enforce a final newline; many CI lint configs fail the build on this. The diff shows }\ with no newline marker on the next line. Add \n after the closing brace.

Suggested change
}
}

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

IInstallationInstructionsService installationInstructionsService)
: BaseContentProvider(contentValidator, installationInstructionsService, logger)
{
private readonly IContentDiscoverer _discoverer = discoverers.OfType<CsvDiscoverer>().FirstOrDefault()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Field initialization silently falls back to any discoverer when CsvDiscoverer is not registered

The _discoverer = discoverers.OfType<CsvDiscoverer>().FirstOrDefault() ?? discoverers.FirstOrDefault(d => SourceName matches CsvConstants.SourceName) ?? throw ... fallback silently picks a non-CSV discoverer (e.g. a GeneralsOnline discoverer) if CsvDiscoverer is missing; the throw only fires when no discoverer exists at all. This makes the CSV provider wire itself to an unrelated source and ship the wrong manifests. Drop the fallback or restrict it to types that implement CSV resolution.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Generic catch (Exception ex) in new code violates CS-R1008

The new catch-all at line 108 (with the dedicated catch (OperationCanceledException) { throw; } already at line 104) hides programming defects (NullReferenceException, ArgumentException) behind a delivery failure. Catch the specific IO/HTTP failures (HttpRequestException, IOException, TaskCanceledException) and let unexpected exceptions propagate so real bugs surface.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

/// <summary>
/// Represents the result of an action set operation.
/// </summary>
public record ActionSetResult

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: ActionSetResult does not extend ResultBase, breaking the result-pattern

Every other fallible operation in GenHub returns OperationResult<T> (or a specialized subtype like LaunchResult, ValidationResult). This new result type is a plain record with Success / ErrorMessage / Details and no relationship to ResultBase. Downstream consumers now need a parallel pattern (if (result.Success && result.ErrorMessage == null)), and this will grow into a permanent exception. Either have ActionSetResult extend ResultBase or expose an OperationResult<ActionSetResult> adapter at the orchestrator boundary.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants