166 lines
5.2 KiB
Markdown
166 lines
5.2 KiB
Markdown
# Remove Uninstall Metadata Design
|
|
|
|
## Goal
|
|
|
|
Make `aim remove` uninstall `aim`-managed app artifacts rather than only unregistering apps, while preserving backward compatibility for existing registry entries and keeping uninstall logic in `aim-core`.
|
|
|
|
## Agreed Product Shape
|
|
|
|
### Remove behavior
|
|
|
|
- `aim remove <QUERY>` should delete the tracked app from the registry
|
|
- It should also remove `aim`-managed installed artifacts for that app
|
|
- Missing artifact files should not make removal fail
|
|
- Desktop refresh helper failures should surface as warnings rather than block removal
|
|
|
|
### Metadata model
|
|
|
|
- Persist install metadata on successful install
|
|
- Persist exact installed paths for payload, desktop entry, and icon
|
|
- Persist the effective install scope
|
|
- Keep install metadata optional so old registry entries still deserialize cleanly
|
|
|
|
### Compatibility posture
|
|
|
|
- New installs should uninstall using persisted exact paths
|
|
- Existing registry entries without install metadata should fall back to derived `aim`-managed paths
|
|
- Uninstall should only target paths that belong to `aim` management, not arbitrary external files
|
|
|
|
## Recommended Approach
|
|
|
|
Use a hybrid uninstall model.
|
|
|
|
This combines exact persisted install metadata for newly installed apps with a conservative derived-path fallback for legacy registry entries. It avoids breaking older registries while making future uninstall behavior precise and resilient to layout changes.
|
|
|
|
The model is:
|
|
|
|
- successful install persists `InstallMetadata` into `AppRecord`
|
|
- remove resolves uninstall targets from metadata when present
|
|
- remove falls back to `aim`-managed path derivation when metadata is absent
|
|
- uninstall deletes payload, desktop entry, and icon, then refreshes desktop caches best-effort
|
|
|
|
## Data Model
|
|
|
|
### `InstallMetadata`
|
|
|
|
Persist this on each installed app record:
|
|
|
|
- `scope`
|
|
- `payload_path`
|
|
- `desktop_entry_path`
|
|
- `icon_path`
|
|
|
|
Store these as optional path strings under an optional `install` field on `AppRecord`.
|
|
|
|
### Backward compatibility
|
|
|
|
- `AppRecord.install` is optional
|
|
- legacy records continue to load unchanged
|
|
- remove derives fallback targets from `stable_id` and inferred scope when `install` is absent
|
|
|
|
## Remove Execution Model
|
|
|
|
### 1. Resolve the app
|
|
|
|
- Match the registered app by stable id or display name
|
|
- Preserve existing ambiguity behavior
|
|
|
|
### 2. Resolve uninstall targets
|
|
|
|
Prefer persisted metadata:
|
|
|
|
- exact payload path
|
|
- exact desktop entry path
|
|
- exact icon path
|
|
- persisted scope
|
|
|
|
Fallback for legacy records:
|
|
|
|
- payload path from `managed_appimage_path(...)`
|
|
- desktop entry path from `desktop_entry_path(...)`
|
|
- icon path from `icon_path(...)`
|
|
- scope inferred conservatively, defaulting to user-managed locations when exact scope is unknown
|
|
|
|
### 3. Delete managed artifacts
|
|
|
|
Delete only `aim`-managed artifacts:
|
|
|
|
- AppImage payload
|
|
- generated `.desktop` file
|
|
- generated icon
|
|
|
|
Rules:
|
|
|
|
- treat missing files as already removed
|
|
- collect deleted paths for reporting
|
|
- do not attempt to delete non-`aim` unmanaged locations
|
|
|
|
### 4. Refresh desktop integration best-effort
|
|
|
|
- probe helpers using the same capability model used by install
|
|
- run `update-desktop-database` and `gtk-update-icon-cache` when relevant
|
|
- return warnings instead of failing removal if refresh helpers are missing or fail
|
|
|
|
### 5. Persist registry last
|
|
|
|
- only save the registry after uninstall execution completes
|
|
- successful uninstall with warnings still removes the registry entry
|
|
|
|
## Safety Model
|
|
|
|
### Managed-only deletion
|
|
|
|
Uninstall must only delete files that are known `aim` outputs:
|
|
|
|
- persisted metadata generated by `aim`
|
|
- derived managed paths under `aim`-controlled naming conventions
|
|
|
|
### Idempotency
|
|
|
|
Repeated remove attempts should be safe:
|
|
|
|
- missing files are ignored
|
|
- warnings can still be emitted for helper refresh failures
|
|
- registry result remains correct even if artifacts were already manually deleted
|
|
|
|
### Failure handling
|
|
|
|
- file deletion failures should stop removal and keep the app registered
|
|
- refresh failures should not block removal
|
|
- no rollback is needed for already-deleted files
|
|
|
|
## Verification Strategy
|
|
|
|
### Unit tests
|
|
|
|
- install metadata round-trips through the registry model
|
|
- remove plan resolves exact persisted paths
|
|
- remove plan falls back to derived managed paths for legacy records
|
|
|
|
### Integration tests
|
|
|
|
- add then remove deletes payload, desktop entry, and icon
|
|
- remove tolerates already-missing artifacts
|
|
- remove emits warnings when refresh helpers are unavailable or fail
|
|
- legacy registry entries without install metadata still uninstall derived user-managed files
|
|
|
|
## Architecture Slice
|
|
|
|
### Core changes
|
|
|
|
- extend `domain::app::AppRecord` with optional `InstallMetadata`
|
|
- populate `InstallMetadata` in the add/install path from `InstalledApp`
|
|
- extend `app::remove` with uninstall planning and execution
|
|
- reuse `integration::refresh` helper execution after deletion
|
|
|
|
### CLI changes
|
|
|
|
- keep CLI thin
|
|
- `aim-cli` should continue delegating uninstall behavior to `aim-core`
|
|
- render warnings from uninstall results if present
|
|
|
|
## Non-Goals
|
|
|
|
- deleting arbitrary external files not managed by `aim`
|
|
- adding a separate garbage collection command in this change
|
|
- refactoring install path layout beyond the metadata needed for uninstall
|