You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature]: Add worktree-isolated identity to winapp run #763
Update: v1 shipped as a simpler staged-manifest transform without ownership records, locks, or journals. See the v1 scope revision comment; sections below about ownership metadata, sidecars, and locks no longer apply.
Is your feature request related to a problem? Please describe.
Multiple coding agents can build and run the same packaged .NET/WinUI app from different Git worktrees on one Windows machine. The worktrees already have separate build outputs and separate loose-layout AppX directories, but their manifests retain the same package identity. Windows therefore treats them as one package family: running one worktree can unregister or replace another worktree's development deployment, redirect AUMID activation, and share package-scoped state.
The current removal paths also include name-scoped operations that can remove more than the exact deployment selected by the caller. This is unsafe once several managed development deployments may coexist.
Describe the solution you'd like
Add explicit opt-in support:
winapp run <input>--unique-identity
The flag lets agents run the same packaged app concurrently from different worktrees. The caller chooses only whether to opt in; winapp resolves and manages the effective identity automatically.
Goals
Allow concurrent loose-layout development deployments of the same packaged .NET/WinUI app from different worktrees.
Preserve existing winapp run behavior unless --unique-identity is supplied.
Give the same resolved app in the same worktree a stable identity across reruns, regardless of whether it was invoked through ., a .csproj, or solution selection.
Keep each worktree's package registration, PFN/AUMIDs, and package-family-scoped app data isolated.
Modify only winapp's staged loose-layout copy. Never modify source files or the host build output.
Register and unregister only the exact development package owned by winapp.
Preserve localized packaged WinUI resources, including resources.pri.
Non-goals for v1
Automatic enablement based on detecting a Git worktree. Agents explicitly pass --unique-identity.
Unpackaged apps, which have no package identity to vary.
Sparse/external-location identity. Sparse identity requires matching <msix> identity embedded into the executable, rewriting the built EXE with mt.exe, invalidating signatures, and creating/signing another identity package.
MSIX/MSIXBundle installation, Store packages, package conversion, or production package rebranding.
Bundles, resource packages, optional packages, or multi-application package selection.
Rewriting public activation contracts such as protocols, file associations, or COM CLSIDs. (Execution aliases are the one exception; see below.)
Sharing or migrating app data between original and derived identities.
Using the derived identity as a public UI-automation selector.
UX and compatibility
winapp run without the flag preserves the manifest's original identity.
--unique-identity is valid only for packaged folder mode and packaged .NET/WinUI project mode.
It is rejected for unpackaged projects, sparse manifests, bundle inputs, and unsupported package shapes before registration.
Equivalent invocations that resolve to the same app use the same identity. The user does not choose an identity key.
The existing per-worktree loose-layout directory remains the staging directory: by default <resolved build output>\AppX, or the existing --output-appx-directory value.
Switching the same staging directory between normal and unique modes first unregisters the exact prior winapp-managed deployment, then rewrites the staged layout. Two registered identities must never reference the same mutable manifest directory.
Execution aliases under --unique-identity: each plain windows.appExecutionAlias on the single application is renamed in the staged layout only, and registration fails before any change if Windows already gives the renamed alias to a different package family. An authored alias foo.exe becomes foo.w<suffix>.exe, where <suffix> is the same 24-hex-character path-derived suffix used in the unique package name; winapp's generated default alias (winapp-<family>.exe) is regenerated from the unique package family instead. --with-alias launches through the renamed alias. (Scope expanded during review of Add worktree-isolated identity to winapp run #872.)
--clean removes data only for the exact effective development package.
--unregister-on-exit unregisters only the exact full package name returned by this run.
Deterministic identity algorithm
Resolve the input using the existing run pipeline.
Project mode: use the canonical resolved .csproj path selected by project/solution resolution.
Folder mode: use the canonical resolved input folder.
Canonicalize the path for Windows identity purposes: absolute path, normalized separators and trailing separator, case-insensitive normalization, and final filesystem target resolution where supported so junction, symlink, short-name, and equivalent path spellings converge.
Read the original manifest identity. The stable family seed is the canonical app path, original Identity/@Name, and exact-case Identity/@Publisher, prefixed by an algorithm version.
Hash the UTF-8 seed with SHA-256 and encode a fixed prefix using a package-name-safe lowercase alphabet.
Set the effective name to <truncated-original>.w<hash>, preserving as much of the original name as possible while staying within the MSIX Identity Name limit of 3-50 characters.
Validate the final package string against Windows rules: only ASCII letters, digits, . and -; no reserved device names or punycode-reserved forms; no trailing dot.
Preserve Publisher, Version, ProcessorArchitecture, ResourceId, and every Application/@Id. Only Identity/@Name changes.
Use Windows package identity APIs such as PackageFamilyNameFromId/PackageFullNameFromId rather than duplicating PFN/full-name algorithms. Each AUMID is <effective PFN>!<Application Id>.
If the derived identity is already associated with a different canonical owner path, fail. Never add a random salt or silently adopt/remove the other deployment.
The algorithm and suffix length must be versioned and covered by golden tests. Once released, changing it is a migration because it changes package family and app-data location.
Transform pipeline
Build/evaluate exactly as winapp run does today. Do not inject identity into source, project properties, or build output.
Copy/synchronize the existing recipe-driven loose layout into the existing per-worktree AppX staging directory.
Acquire a per-layout/per-effective-identity lock so concurrent invocations cannot race manifest transformation, registration, or sidecar updates.
Read and preflight the staged manifest before changing it.
Rewrite only staged Identity/@Name through AppxManifestDocument.
If resources.pri exists, regenerate it under the effective package name while preserving all resource names, qualifiers, values, languages, and paths from the MSBuild-produced PRI. This step is mandatory and fail-closed.
Validate the transformed manifest, effective package identity, PRI root map/languages, executable and assets before registration.
Unregister only the exact prior package recorded for this layout when replacement is required.
Register the staged manifest in DevelopmentMode and capture the resulting exact package full name and install location.
Compute/select the AUMID and launch through the existing path.
Atomically persist ownership metadata only after successful registration. On failure, retain enough prior metadata to clean up safely and report any orphaned exact full name.
Normal fully packaged WinUI/.NET loose-layout apps do not require an embedded <msix> fusion-manifest identity. Embedded identity is a sparse-package concern and is deferred.
PRI/resource fidelity requirement
Changing Identity/@Name changes the default authority for ms-resource and ms-appx resolution. Copying the original identity-bound resources.pri is unsafe, while the current fallback PRI generator indexes only a limited set of image resources and cannot reconstruct a general localized MSBuild/WinUI resource graph.
Before implementation, complete a blocking spike using makepri's PRI indexer:
Build a packaged WinUI sample containing multiple .resw languages, manifest ms-resource: values, XAML resources, qualified images, and ms-appx references.
Stage its MSBuild-produced layout and preserve the original PRI as a separate input.
Generate a new PRI with an explicit /in <effective-package-name> and a config that indexes the existing PRI without auto-splitting resource packages.
Compare detailed dumps and prove that the new root map uses the effective identity and that all languages, candidates, qualifiers, values, and paths survive.
Register and launch two renamed worktree layouts concurrently and exercise localized UI/resource lookup.
If re-indexing the existing PRI cannot preserve full fidelity, the fallback design is an MSBuild-native temporary identity substitution before MakePri runs. The feature must not ship with best-effort PRI generation or a warning-only failure.
Supported and unsupported manifest surfaces
V1 supports package-scoped activation that becomes isolated through the new PFN/AUMID. It does not silently rewrite public contracts.
Preflight all application-level and package-level extensions. Conservatively fail --unique-identity when the manifest declares a surface whose concurrent behavior cannot be proven isolated, including:
uap5:AppExecutionAlias with anything beyond a plain ExecutionAlias (plain aliases are renamed; see above)
packaged COM servers/extensions and CLSIDs
protocols and file type associations
Windows services
startup tasks
shell/context-menu integrations
any other extension that registers a machine/user-global public name
App services, background tasks, notifications, and similar PFN/AUMID-scoped surfaces may be allowed only after their scoping is verified. Emit a clear warning that external clients hardcoding the original PFN/AUMID will not target the unique deployment.
Errors must name the unsupported manifest element/category and explain that winapp did not rewrite its public contract. V1 has no --allow-conflicts escape hatch.
Ownership, conflicts, reruns, and cleanup
Persist an atomic sidecar adjacent to the staging directory, not inside package content. Suggested fields:
schema/algorithm version
identity mode
canonical owner app path
staging layout path
original package identity values
effective package identity values
effective PFN, application ID/AUMID, and exact package full name
registered install location
transformed manifest hash
registration/update timestamps
Safety rules:
Treat the sidecar as a hint, not authority. Before removal verify the installed package's exact full name, IsDevelopmentMode, and install location matches the owned staging directory.
Use UnregisterByFullNameAsync/RemovePackageAsync(fullName, ...); never remove by name, prefix, family, or an enumeration wider than the selected exact package.
Never remove Store/signed/non-development packages.
A rerun with the same effective identity and unchanged manifest/layout may use the existing skip-registration optimization.
Payload-only updates synchronize the staged files without changing identity.
Manifest/identity changes perform exact unregister then register.
If another active winapp-managed deployment owns the original or effective identity from a different path, fail with its full name and owner/install path. For an original-identity collision, guide the caller to rerun with --unique-identity; never silently evict the other worktree.
If the same layout switches identity mode, remove its exact previously owned deployment before changing the staged manifest.
On hash collision, report both canonical paths and fail rather than changing the deterministic identity.
Explicit winapp unregister should consult matching ownership metadata and remove exact owned deployments only. Whether cleanup uses an added unregister --unique-identity flag or transparent sidecar discovery is an implementation UX decision, but name-scoped removal is not acceptable.
JSON additions
Keep the existing top-level AUMID, ProcessId, and Error fields. Add an optional additive Identity object for packaged runs:
Do not expose the internal hash/key as a UI-targeting contract. Human output should identify unique mode and the effective PFN/AUMID, with paths reserved for verbose output and conflict guidance.
Errors and exit behavior
Return nonzero, with equivalent structured --json errors, for:
unpackaged, sparse, bundle, multi-app, or otherwise unsupported input
unsupported public/global activation surface
invalid or over-length derived identity
canonicalization failure that prevents a stable key
managed identity owned by another path
PRI transformation or fidelity validation failure
registration state that cannot be proven safe to replace/remove
sidecar/installed-package disagreement
Errors should be actionable and include the relevant owner path, install location, exact package full name, manifest category/XPath, and safe remediation when available.
Fidelity and security caveats
Package-family-scoped WinRT app data is isolated automatically. Raw Win32 writes to shared %LOCALAPPDATA%, registry locations, files, ports, mutexes, named pipes, databases, and external services are not isolated by package identity.
Capability consent, notifications, protocol defaults, and other OS/user state do not migrate from the original identity.
Canonical path handling must resist alternate spellings, junctions, symlinks, 8.3 names, and case differences.
Sidecar tampering must never authorize broader removal; verify live package metadata before destructive operations.
Registration and sidecar writes require locking and atomic replacement for concurrent agents and crash recovery.
DevelopmentMode requires a supported local layout filesystem/location. Surface platform errors clearly.
Never log secrets from MSBuild properties or package configuration.
--clean and --unregister-on-exit affect only one worktree
default run detects another managed owner and recommends --unique-identity
DeveloperMode disabled and unsupported filesystem/location failures
x64, x86, and arm64 where CI permits
explicit --output-appx-directory
unsupported protocol/file-association/COM/service/startup manifests, and alias declarations beyond a plain ExecutionAlias, fail before registration
Sample/guide test:
clone/copy a packaged WinUI sample into two worktree-like paths, run both with --unique-identity --no-launch --json, assert different effective PFNs and exact registrations, then clean each independently.
Required pre-implementation spikes
Prove lossless identity-aware PRI re-indexing for localized packaged WinUI as described above.
Measure duplicate registration behavior for execution aliases, packaged COM CLSIDs, protocols/file associations, app services, services, and startup tasks; use results to refine the conservative support table.
Validate canonical path resolution across junctions, symlinks, subst drives, 8.3 paths, and worktrees on common developer filesystems.
Verify Windows package identity API use under the CLI's NativeAOT constraints.
Verify exact rerun/removal behavior when a development package is active or files are in use.
Additional context
Current repository behavior already provides most of the needed seams:
Folder/project runs stage into separate per-worktree AppX directories, but register the unchanged package identity.
MSBuild-generated layouts are copied from .build.appxrecipe, preserving packaged files and existing localized PRI artifacts.
PackageRegistrationService.UnregisterByFullNameAsync already exists, but several run/unregister paths still call broader name-scoped removal.
Current fallback PRI generation is intentionally insufficient for this feature because it does not preserve a general localized WinUI resource graph or set an explicit effective index name.
Normal packaged loose-layout WinUI apps do not need embedded <msix> identity; sparse apps do and are deferred.
Relevant Windows rules:
MSIX Identity/@Name is 3-50 characters and forms the PFN with the publisher ID.
AUMID is <package family name>!<Application Id>.
DevelopmentMode loose-layout registration does not require package signing, but requires a supported local development layout and cannot be used with bundles.
resources.pri top-level resource-map authority typically corresponds to package identity and must be regenerated when the identity name changes.
Package removal APIs operate on exact package full names; app-data preservation is valid only for DevelopmentMode packages.
Open implementation decisions that should be resolved by the spikes rather than guessed:
exact safe hash encoding/length and canonical filesystem API
PRI re-index configuration versus temporary MSBuild identity injection fallback
explicit versus sidecar-discovered winapp unregister UX
which currently conservative manifest surfaces can be safely supported after measured conflict behavior
Data points from a consumer that hit this exact problem and ended up solving it locally, in case they are useful for the two blocking spikes. We are a packaged WinUI test host in microsoft/microsoft-ui-reactor; the change is microsoft/microsoft-ui-reactor#1260.
1. The uap5:AppExecutionAlias rule excludes us, and alias support may not be separable from the goal.
We are exactly the target audience (two git worktrees, one packaged WinUI app, agents running them concurrently), but our manifest declares an execution alias, so v1 would conservatively fail us per the unsupported-surface table.
For us the alias is not cosmetic. Launching the alias stub keeps package identity and inherits stdout/stderr, argv and exit code; AUMID activation is brokered, so stdout cannot be redirected at all. Our whole packaged test tier depends on capturing TAP output from a process that has identity, so "register with --unique-identity, launch by AUMID" is not a workaround for this shape of app.
More relevant to the design: deriving only Identity/@Name does not actually isolate an app that declares an alias. The alias is a single filename in a flat per-user directory (%LOCALAPPDATA%\Microsoft\WindowsApps, 66 flat entries on this machine alongside 27 PFN-scoped subdirectories). Two worktrees would get distinct PFNs and app data, then still contend for one stub, so whichever registered last owns the name. We had to derive the alias as well as the name to get the isolation the flag is for. That is a concrete case for spike 2: for alias-declaring apps, refusing the flag and rewriting the alias may be the only two coherent options, and refusing leaves the stated goal unmet.
2. PRI fidelity: a narrow counter-data-point, which does not clear spike 1.
We rename staged Identity/@Name and do not regenerate resources.pri (1.36 MB, merged framework PRIs including Microsoft.UI.Xaml.Controls.pri). 1584 packaged fixtures pass under the renamed identity.
The load-bearing one is a fixture that resolves ms-appx:///Assets/...ico through MRT under package identity and compares the resulting icon pixels against the same file addressed by filesystem path, with a zero control proving the probe can still observe "no icon". It is not a liveness check, and it passed renamed.
Bounding that honestly, because it is the part that matters: we have zero .resw, no ms-resource: resolution under package identity, and no multi-language candidates. So this is evidence only for the unlocalized case where the PRI is framework content plus assets. It says nothing about the localized MSBuild/WinUI resource graph your spike is actually about, and it is not a reason to relax the fail-closed requirement.
What it might be worth: the unlocalized case appears to survive an identity rename with the original PRI intact. If that generalizes, "no .resw and no ms-resource:" could be a cheap preflight that skips PRI re-indexing entirely rather than blocking the feature on the general solution.
3. Cheap corroboration on name-scoped removal.
Independently of this issue we had the bug described in the cleanup section: removal scoped to name plus publisher, which evicted another worktree's live registration. Agreeing from experience that name-scoped removal is not acceptable. What we landed on was a single publisher-filtered sweep with three rules (this layout's derived name; any package installed from this exact directory; derived-shaped packages whose install directory no longer exists), none of which can reach a live concurrent checkout.
The second rule turned out to be necessary rather than nice-to-have: without it, the first run after adopting derived identities finds a stale base-name registration owning the same layout directory, and registering over it fails in a way that killed our host mid-run. Worth considering for the mode-switch path, which has the same shape.
The third matters more once identities are per-path: one shared registration becomes one per worktree, so deleting a worktree leaks a registration unless something reclaims it.
Confirming the expanded v1 scope from review of #872: execution aliases are in scope for --unique-identity.
Supported: a plain windows.appExecutionAlias extension (one AppExecutionAlias with plain ExecutionAlias elements) on the manifest's single application. Anything richer is still rejected before registration.
Renamed in staging only: the source manifest is unchanged; the staged appxmanifest.xml gets the derived alias.
Naming rule: an authored alias foo.exe becomes foo.w<suffix>.exe, where <suffix> is the same 24-hex-character suffix used for the unique package name (first 12 bytes of SHA-256 over the canonical checkout path, original Identity/@Name, and publisher). The stem is truncated if needed to stay a valid filename. If the authored alias is winapp's own generated default (winapp-<family>.exe), it is regenerated from the unique package family instead. The name is stable for a given checkout path, and run prints each original -> renamed mapping.
Ownership check: before any package change, winapp checks each renamed alias in %LOCALAPPDATA%\Microsoft\WindowsApps. If it exists and its owner is not the expected unique package family (or the owner can't be verified), the run fails and nothing is changed.
--with-alias works under --unique-identity and launches through the renamed alias.
I've updated the non-goals, the --with-alias note, and the support table in the issue body to match.
v1 scope revision (#872). The implementation was simplified after review, and this supersedes my earlier comment on aliases and naming.
What v1 does:
winapp run --unique-identity derives <first 24 chars of Identity/@Name>.w<24 hex> from the canonical checkout path (the .csproj/.vcxproj/.cs file, or the input folder) and the original name. The publisher is not part of the hash, because Windows already keeps packages from different publishers apart by package family.
Only the staged layout changes: Identity/@Name, authored execution aliases (tool.exe -> tool.w<hash>.exe), and resources.pri, which is re-indexed with one makepri call. Then the normal register flow runs under the derived name.
The generated default alias comes from the new package family. Alias availability is checked before launching through an alias, the same as in a plain run.
unregister with the same project, .cs file, or folder also looks for the derived name. No extra flag is needed.
Sandbox: the host stages the renamed layout and the guest registers it unchanged. No guest-agent changes.
Dropped from the original spec: ownership metadata/sidecars, locks, atomic journals and crash recovery, revision fencing, and guest capability negotiation. The derived name already encodes the checkout path, so Windows' registration state is enough, and plain winapp run stays exactly as before. Spec sections on those mechanisms (ownership, sidecar atomicity, lock contention, ownership-aware cleanup) no longer apply to v1.
Is your feature request related to a problem? Please describe.
Multiple coding agents can build and run the same packaged .NET/WinUI app from different Git worktrees on one Windows machine. The worktrees already have separate build outputs and separate loose-layout
AppXdirectories, but their manifests retain the same package identity. Windows therefore treats them as one package family: running one worktree can unregister or replace another worktree's development deployment, redirect AUMID activation, and share package-scoped state.The current removal paths also include name-scoped operations that can remove more than the exact deployment selected by the caller. This is unsafe once several managed development deployments may coexist.
Describe the solution you'd like
Add explicit opt-in support:
The flag lets agents run the same packaged app concurrently from different worktrees. The caller chooses only whether to opt in; winapp resolves and manages the effective identity automatically.
Goals
winapp runbehavior unless--unique-identityis supplied.., a.csproj, or solution selection.resources.pri.Non-goals for v1
--unique-identity.<msix>identity embedded into the executable, rewriting the built EXE withmt.exe, invalidating signatures, and creating/signing another identity package.UX and compatibility
winapp runwithout the flag preserves the manifest's original identity.--unique-identityis valid only for packaged folder mode and packaged .NET/WinUI project mode.<resolved build output>\AppX, or the existing--output-appx-directoryvalue.--unique-identity: each plainwindows.appExecutionAliason the single application is renamed in the staged layout only, and registration fails before any change if Windows already gives the renamed alias to a different package family. An authored aliasfoo.exebecomesfoo.w<suffix>.exe, where<suffix>is the same 24-hex-character path-derived suffix used in the unique package name; winapp's generated default alias (winapp-<family>.exe) is regenerated from the unique package family instead.--with-aliaslaunches through the renamed alias. (Scope expanded during review of Add worktree-isolated identity to winapp run #872.)--cleanremoves data only for the exact effective development package.--unregister-on-exitunregisters only the exact full package name returned by this run.Deterministic identity algorithm
.csprojpath selected by project/solution resolution.Identity/@Name, and exact-caseIdentity/@Publisher, prefixed by an algorithm version.<truncated-original>.w<hash>, preserving as much of the original name as possible while staying within the MSIXIdentity Namelimit of 3-50 characters..and-; no reserved device names or punycode-reserved forms; no trailing dot.Application/@Id. OnlyIdentity/@Namechanges.PackageFamilyNameFromId/PackageFullNameFromIdrather than duplicating PFN/full-name algorithms. Each AUMID is<effective PFN>!<Application Id>.The algorithm and suffix length must be versioned and covered by golden tests. Once released, changing it is a migration because it changes package family and app-data location.
Transform pipeline
winapp rundoes today. Do not inject identity into source, project properties, or build output.AppXstaging directory.Identity/@NamethroughAppxManifestDocument.resources.priexists, regenerate it under the effective package name while preserving all resource names, qualifiers, values, languages, and paths from the MSBuild-produced PRI. This step is mandatory and fail-closed.Normal fully packaged WinUI/.NET loose-layout apps do not require an embedded
<msix>fusion-manifest identity. Embedded identity is a sparse-package concern and is deferred.PRI/resource fidelity requirement
Changing
Identity/@Namechanges the default authority forms-resourceandms-appxresolution. Copying the original identity-boundresources.priis unsafe, while the current fallback PRI generator indexes only a limited set of image resources and cannot reconstruct a general localized MSBuild/WinUI resource graph.Before implementation, complete a blocking spike using
makepri's PRI indexer:.reswlanguages, manifestms-resource:values, XAML resources, qualified images, andms-appxreferences./in <effective-package-name>and a config that indexes the existing PRI without auto-splitting resource packages.If re-indexing the existing PRI cannot preserve full fidelity, the fallback design is an MSBuild-native temporary identity substitution before MakePri runs. The feature must not ship with best-effort PRI generation or a warning-only failure.
Supported and unsupported manifest surfaces
V1 supports package-scoped activation that becomes isolated through the new PFN/AUMID. It does not silently rewrite public contracts.
Preflight all application-level and package-level extensions. Conservatively fail
--unique-identitywhen the manifest declares a surface whose concurrent behavior cannot be proven isolated, including:uap5:AppExecutionAliaswith anything beyond a plainExecutionAlias(plain aliases are renamed; see above)App services, background tasks, notifications, and similar PFN/AUMID-scoped surfaces may be allowed only after their scoping is verified. Emit a clear warning that external clients hardcoding the original PFN/AUMID will not target the unique deployment.
Errors must name the unsupported manifest element/category and explain that winapp did not rewrite its public contract. V1 has no
--allow-conflictsescape hatch.Ownership, conflicts, reruns, and cleanup
Persist an atomic sidecar adjacent to the staging directory, not inside package content. Suggested fields:
Safety rules:
IsDevelopmentMode, and install location matches the owned staging directory.UnregisterByFullNameAsync/RemovePackageAsync(fullName, ...); never remove by name, prefix, family, or an enumeration wider than the selected exact package.--unique-identity; never silently evict the other worktree.Explicit
winapp unregistershould consult matching ownership metadata and remove exact owned deployments only. Whether cleanup uses an addedunregister --unique-identityflag or transparent sidecar discovery is an implementation UX decision, but name-scoped removal is not acceptable.JSON additions
Keep the existing top-level
AUMID,ProcessId, andErrorfields. Add an optional additiveIdentityobject for packaged runs:{ "AUMID": "Effective.Name_abcd1234!App", "ProcessId": 1234, "Identity": { "Mode": "Unique", "OriginalPackageName": "Original.Name", "EffectivePackageName": "Original.Name.w0123456789abcdef", "Publisher": "CN=Contoso", "PackageFamilyName": "Original.Name.w0123456789abcdef_abcd1234", "PackageFullName": "...", "ApplicationId": "App", "OwnerPath": "C:\\repo-worktree\\App.csproj", "LayoutPath": "C:\\repo-worktree\\bin\\Debug\\...\\AppX" } }Do not expose the internal hash/key as a UI-targeting contract. Human output should identify unique mode and the effective PFN/AUMID, with paths reserved for verbose output and conflict guidance.
Errors and exit behavior
Return nonzero, with equivalent structured
--jsonerrors, for:Errors should be actionable and include the relevant owner path, install location, exact package full name, manifest category/XPath, and safe remediation when available.
Fidelity and security caveats
%LOCALAPPDATA%, registry locations, files, ports, mutexes, named pipes, databases, and external services are not isolated by package identity.Implementation areas
RunCommand.cs/RunCommand.ProjectMode.cs: option, validation, threading, UX, JSON, and exact cleanup.MsixService.Identity.cs/ skip-registration path: staged transform, ownership-aware rerun, exact replacement.AppxManifestDocument.cs: all-application identity/extension inspection and safe staged identity mutation.PriService.cs/MrtAssetHelper.cs: identity-aware PRI re-indexing, dump validation, and fidelity checks.PackageRegistrationService.cs/IPackageRegistrationService.cs: exact full-name lookup/removal and live ownership verification.UnregisterCommand.cs: eliminate name-scoped removal and integrate managed sidecars.RunOptions, generated docs/schema, usage docs, plugin skills, and relevant samples.Test matrix
Unit tests:
Identity/@NameIntegration tests on Windows:
.resw, XAML, manifest strings, MRT-qualified assets, andms-appxreferences retain fidelity--cleanand--unregister-on-exitaffect only one worktree--unique-identity--output-appx-directoryExecutionAlias, fail before registrationSample/guide test:
--unique-identity --no-launch --json, assert different effective PFNs and exact registrations, then clean each independently.Required pre-implementation spikes
Additional context
Current repository behavior already provides most of the needed seams:
AppXdirectories, but register the unchanged package identity..build.appxrecipe, preserving packaged files and existing localized PRI artifacts.AppxManifestDocumentalready provides namespace-aware identity access.PackageRegistrationService.UnregisterByFullNameAsyncalready exists, but several run/unregister paths still call broader name-scoped removal.<msix>identity; sparse apps do and are deferred.Relevant Windows rules:
Identity/@Nameis 3-50 characters and forms the PFN with the publisher ID.<package family name>!<Application Id>.resources.pritop-level resource-map authority typically corresponds to package identity and must be regenerated when the identity name changes.Open implementation decisions that should be resolved by the spikes rather than guessed:
winapp unregisterUX