Skip to content

Fall back from unwritable installation-adjacent CAS storage and migrate persisted paths #347

Description

@bobtista

Problem

GenHub automatically selects an installation-adjacent CAS pool at <installation>/.genhub-cas.

Both ContentOrchestrator and CommunityOutpostDeliverer persist that location as UserSettings.CasConfiguration.InstallationPoolRootPath and mark the automatically derived value as explicitly set.

CasPoolResolver.IsInstallationPoolAvailable() currently considers any nonempty path available. It does not verify that the packaged application can create or modify the pool.

The resolver routes these content types to the installation pool when that setting is nonempty:

  • GameInstallation
  • GameClient
  • Addon
  • Patch
  • Map
  • Mod

When the installation is under Program Files, a packaged non-administrator process may be able to read the installation but cannot write <installation>/.genhub-cas. Content acquisition and CAS maintenance can therefore fail.

Launches using already-populated content appear to require only CAS reads during workspace preparation, but that must be verified with the packaged Windows build rather than treated as established behavior.

Existing-user impact

This is not only a clean-install path-selection problem.

Users may already have an unwritable installation-adjacent path persisted in InstallationPoolRootPath. Changing the default resolver alone will not repair those settings.

Changing the selected pool can also leave existing CAS objects under the previous root. Migration must not silently delete or orphan usable content.

Expected behavior

GenHub should prefer installation-adjacent CAS storage when it is writable, preserving same-volume hard-link efficiency.

When that location is not writable, GenHub should select a user-writable CAS pool. Cross-volume workspace materialization may use the copy fallback enabled by #346.

Automatically derived paths should not be treated as permanent user overrides.

Requirements

  • Probe an installation-adjacent CAS location before persisting or adopting it.
  • Treat an empty, invalid, missing, or unwritable installation-pool path as unavailable.
  • Fall back to a configured writable CAS root or the application-data CAS pool.
  • Distinguish automatically derived paths from paths deliberately configured by the user.
  • Detect previously persisted auto-derived paths that point into a protected installation.
  • Migrate or clear those settings and reinitialize the installation pool without requiring users to reset their configuration manually.
  • Define safe handling for CAS objects under the previous root:
    • do not delete existing content automatically;
    • preserve readable content, migrate it safely, or allow it to be reacquired;
    • avoid leaving manifests or references pointing at an inaccessible pool.
  • Log the selected pool and the reason for fallback without repeatedly probing on every operation.
  • Preserve writable installation-adjacent pools.

Acceptance criteria

  • A packaged non-administrator Windows build can acquire GameClient content when the game is installed under Program Files.
  • All installation-pool content types resolve to a writable effective pool.
  • A writable installation-adjacent pool remains selected.
  • A writable user-configured CAS path remains honored.
  • An existing persisted protected path is repaired automatically.
  • CasPoolManager is reinitialized after the effective installation-pool path changes.
  • Existing CAS content is not deleted or silently orphaned during migration.
  • CAS garbage collection does not attempt to mutate an unwritable protected pool.
  • Cross-volume workspace preparation succeeds through the copy path.
  • Tests cover clean settings, migrated settings, writable adjacent storage, unwritable adjacent storage, custom CAS storage, and pool reinitialization.
  • Packaged Windows validation covers acquisition followed by profile creation and launch.

Related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions