[GH-ISSUE #3308] [Bug] Uninstall reports a retained skill manifest as removed after partial removal #1181

Closed
opened 2026-10-15 18:31:48 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @coygeek on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3308

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

uninstall --json reports the shared REA skill as removed and the overall operation as complete when a directory containing caller-owned data occupies one managed reference-file path. It removes another managed reference but retains SKILL.md and the obstructing directory. The result discards the retention reason and says the managed skill files were removed even though the ownership manifest remains installed.

The observed command exits 0 with top-level status: "complete" and a skill item with status: "removed". The shared destination's detail says removed (REA-managed skill files were removed; other files were preserved.). The ordinary managed reference is gone, SKILL.md remains, and the directory still contains the unchanged caller.txt bytes. The result contains neither the retained managed-path diagnostic nor the reason for retaining the manifest.

Rerunning uninstall without changing the fixture also exits 0 with status: "complete". The skill item changes to skipped, but its detail still says the managed files were removed, and SKILL.md remains.

Replacing the obstructing directory with a regular reference file allowed uninstall to remove the manifest in the observed control. A second observed recovery moved the directory to saved-caller-reference at the isolated home's root and reran uninstall. The manifest then disappeared, and the saved caller file still contained keep this user file.

Reproduction

  1. Create a clean source checkout and build the runtime:
git clone https://github.com/morluto/rea.git rea-uninstall-repro
cd rea-uninstall-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. Run the following from the repository root. It creates an isolated home, a recognized REA ownership manifest, one ordinary managed reference, and a directory containing caller-owned data at another managed reference path:
work_dir=$(mktemp -d)
skill_root="$work_dir/home/.agents/skills/reverse-engineer-anything"
mkdir -p "$skill_root/references/connection-and-recovery.md"
cat > "$skill_root/SKILL.md" <<'EOF'
---
name: reverse-engineer-anything
---
# REA
EOF
printf 'managed reference\n' > "$skill_root/references/native-and-artifacts.md"
printf 'keep this user file\n' > "$skill_root/references/connection-and-recovery.md/caller.txt"
env -i PATH="$PATH" HOME="$work_dir/home" \
  node scripts/rea.mjs uninstall --json
printf 'uninstall exit: %s\n' "$?"
test -f "$skill_root/SKILL.md" && printf 'manifest remains\n'
test ! -e "$skill_root/references/native-and-artifacts.md" && printf 'ordinary reference removed\n'
cat "$skill_root/references/connection-and-recovery.md/caller.txt"
  1. Repeat uninstall without changing the fixture and check the retained manifest:
env -i PATH="$PATH" HOME="$work_dir/home" \
  node scripts/rea.mjs uninstall --json
printf 'uninstall exit: %s\n' "$?"
test -f "$skill_root/SKILL.md" && printf 'manifest remains\n'
  1. Check recovery by moving the obstruction aside and rerunning uninstall:
mv "$skill_root/references/connection-and-recovery.md" "$work_dir/home/saved-caller-reference"
env -i PATH="$PATH" HOME="$work_dir/home" \
  node scripts/rea.mjs uninstall --json
test ! -e "$skill_root/SKILL.md" && printf 'manifest removed\n'
cat "$work_dir/home/saved-caller-reference/caller.txt"
  1. For the regular-file control, recreate the fixture from step 2 in a fresh isolated home, but before invoking uninstall move the obstructing directory to that home's saved-caller-reference and create the regular reference file with printf 'managed reference\n' > "$skill_root/references/connection-and-recovery.md". Run the same uninstall command and check that the manifest is removed.

Expected behavior

Uninstall should preserve the caller-owned directory and its contents and report that skill removal is incomplete because the managed reference path and ownership manifest were retained. The result should distinguish this state from a fully removed skill and retain the actionable reason. Preserving the obstructing directory is correct.

docs/installation.md:340, under "Diagnose, update, and remove", promises removal of REA-owned skill files while preserving unrelated skills and data. The uninstall --help description says "Remove REA-owned agent configuration and skill files". src/application/Uninstall.ts:745 explicitly retains the ownership manifest when a managed file cannot be safely removed, so the operation already knows the retained state.

Impact and scope

  • Impact level: P2.
  • Assessment status: supported.
  • Impact basis: Uninstall reports complete skill removal while leaving the recognized ownership manifest installed. A caller cannot identify the retained path or required cleanup from the result, and repeating the unchanged command leaves the same obstruction. The observed impact is bounded to managed reference paths occupied by nonregular entries.
  • Workaround status: available.
  • Workaround basis: Moving the obstructing directory aside and rerunning uninstall was verified to remove the manifest while preserving the caller's file and its exact content.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0 on macOS 27.0.1 arm64, with an isolated home. The compiled runtime reports REA 6.4.0. No client registration or external analysis engine is needed.

Target and artifact context

A recognized reverse-engineer-anything ownership manifest in the shared skill destination, one regular managed reference file, and caller-owned caller.txt inside a directory occupying references/connection-and-recovery.md.

Interface

CLI

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

The observed first uninstall returns top-level status: "complete" and a skill item with status: "removed"; the shared destination detail and filesystem checks show:

removed (REA-managed skill files were removed; other files were preserved.)
uninstall exit: 0
manifest remains
ordinary reference removed
keep this user file

The unchanged rerun also exits 0 and reports complete; its skill item becomes skipped, while its detail still says the managed files were removed and the manifest remains. The CLI reproduction and filesystem checks establish the failure. Evidence provenance is observed, local verification reproduced the behavior, and the reproduction is complete.

Typed outcome or recovery expected

Run the directory-obstruction fixture. The caller's file must remain unchanged, the manifest's retention must remain visible in the result, and the skill must not be represented as fully removed. Repeat with an ordinary regular reference file to verify that a fully removed skill still reports removal. After moving the obstruction aside and rerunning, the manifest must be removed and the caller's saved file must remain unchanged.

Analysis or suspected cause

Source-derived reasoning: src/application/Uninstall.ts:745 retains the ownership manifest when another managed file has a retained or failed outcome. src/application/Uninstall.ts:771 replaces the accumulated per-file outcomes with a generic success detail and omits the retained outcomes. That projection explains why a partially removed skill reports removed, and why a later unchanged run reports skipped with the same success wording. These source locators explain the observed output; no patched restoration is claimed.

Additional context

  • #2111, closed as completed, concerns missing directory names for visible retained or failed skill destinations; this report loses the retention status and reason after partial managed-file removal.
  • #2159, closed as completed, concerns empty directory trees left after all managed files were removed; this report retains the ownership manifest and a managed reference path containing caller data.
  • #3057, closed as completed, concerns setup following a symlinked skill root that uninstall explicitly reports as retained; this report uses a regular skill root and hides the reference-path obstruction.
  • #661, closed as completed, concerns per-command skills, links, and a separate manifest omitted by uninstall; this report concerns the recognized canonical skill's managed-file result.
  • #2664, closed as completed, concerns an owned ZCode fallback registration omitted by uninstall; this report concerns retained canonical skill-file outcomes.
  • #3301, open, concerns positive uninstall test fixtures with unrecognized registration commands; this reproduction needs no client registration and establishes skill ownership before partial removal.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3308 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior `uninstall --json` reports the shared REA skill as `removed` and the overall operation as `complete` when a directory containing caller-owned data occupies one managed reference-file path. It removes another managed reference but retains `SKILL.md` and the obstructing directory. The result discards the retention reason and says the managed skill files were removed even though the ownership manifest remains installed. The observed command exits 0 with top-level `status: "complete"` and a `skill` item with `status: "removed"`. The shared destination's detail says `removed (REA-managed skill files were removed; other files were preserved.)`. The ordinary managed reference is gone, `SKILL.md` remains, and the directory still contains the unchanged `caller.txt` bytes. The result contains neither the retained managed-path diagnostic nor the reason for retaining the manifest. Rerunning uninstall without changing the fixture also exits 0 with `status: "complete"`. The skill item changes to `skipped`, but its detail still says the managed files were removed, and `SKILL.md` remains. Replacing the obstructing directory with a regular reference file allowed uninstall to remove the manifest in the observed control. A second observed recovery moved the directory to `saved-caller-reference` at the isolated home's root and reran uninstall. The manifest then disappeared, and the saved caller file still contained `keep this user file`. ### Reproduction 1. Create a clean source checkout and build the runtime: ```bash git clone https://github.com/morluto/rea.git rea-uninstall-repro cd rea-uninstall-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. Run the following from the repository root. It creates an isolated home, a recognized REA ownership manifest, one ordinary managed reference, and a directory containing caller-owned data at another managed reference path: ```bash work_dir=$(mktemp -d) skill_root="$work_dir/home/.agents/skills/reverse-engineer-anything" mkdir -p "$skill_root/references/connection-and-recovery.md" cat > "$skill_root/SKILL.md" <<'EOF' --- name: reverse-engineer-anything --- # REA EOF printf 'managed reference\n' > "$skill_root/references/native-and-artifacts.md" printf 'keep this user file\n' > "$skill_root/references/connection-and-recovery.md/caller.txt" env -i PATH="$PATH" HOME="$work_dir/home" \ node scripts/rea.mjs uninstall --json printf 'uninstall exit: %s\n' "$?" test -f "$skill_root/SKILL.md" && printf 'manifest remains\n' test ! -e "$skill_root/references/native-and-artifacts.md" && printf 'ordinary reference removed\n' cat "$skill_root/references/connection-and-recovery.md/caller.txt" ``` 3. Repeat uninstall without changing the fixture and check the retained manifest: ```bash env -i PATH="$PATH" HOME="$work_dir/home" \ node scripts/rea.mjs uninstall --json printf 'uninstall exit: %s\n' "$?" test -f "$skill_root/SKILL.md" && printf 'manifest remains\n' ``` 4. Check recovery by moving the obstruction aside and rerunning uninstall: ```bash mv "$skill_root/references/connection-and-recovery.md" "$work_dir/home/saved-caller-reference" env -i PATH="$PATH" HOME="$work_dir/home" \ node scripts/rea.mjs uninstall --json test ! -e "$skill_root/SKILL.md" && printf 'manifest removed\n' cat "$work_dir/home/saved-caller-reference/caller.txt" ``` 5. For the regular-file control, recreate the fixture from step 2 in a fresh isolated home, but before invoking uninstall move the obstructing directory to that home's `saved-caller-reference` and create the regular reference file with `printf 'managed reference\n' > "$skill_root/references/connection-and-recovery.md"`. Run the same uninstall command and check that the manifest is removed. ### Expected behavior Uninstall should preserve the caller-owned directory and its contents and report that skill removal is incomplete because the managed reference path and ownership manifest were retained. The result should distinguish this state from a fully removed skill and retain the actionable reason. Preserving the obstructing directory is correct. `docs/installation.md:340`, under "Diagnose, update, and remove", promises removal of REA-owned skill files while preserving unrelated skills and data. The `uninstall --help` description says "Remove REA-owned agent configuration and skill files". `src/application/Uninstall.ts:745` explicitly retains the ownership manifest when a managed file cannot be safely removed, so the operation already knows the retained state. ### Impact and scope - Impact level: P2. - Assessment status: supported. - Impact basis: Uninstall reports complete skill removal while leaving the recognized ownership manifest installed. A caller cannot identify the retained path or required cleanup from the result, and repeating the unchanged command leaves the same obstruction. The observed impact is bounded to managed reference paths occupied by nonregular entries. - Workaround status: available. - Workaround basis: Moving the obstructing directory aside and rerunning uninstall was verified to remove the manifest while preserving the caller's file and its exact content. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0 on macOS 27.0.1 arm64, with an isolated home. The compiled runtime reports REA 6.4.0. No client registration or external analysis engine is needed. ### Target and artifact context A recognized `reverse-engineer-anything` ownership manifest in the shared skill destination, one regular managed reference file, and caller-owned `caller.txt` inside a directory occupying `references/connection-and-recovery.md`. ### Interface CLI ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence The observed first uninstall returns top-level `status: "complete"` and a skill item with `status: "removed"`; the shared destination detail and filesystem checks show: ```text removed (REA-managed skill files were removed; other files were preserved.) uninstall exit: 0 manifest remains ordinary reference removed keep this user file ``` The unchanged rerun also exits 0 and reports `complete`; its skill item becomes `skipped`, while its detail still says the managed files were removed and the manifest remains. The CLI reproduction and filesystem checks establish the failure. Evidence provenance is observed, local verification reproduced the behavior, and the reproduction is complete. ### Typed outcome or recovery expected Run the directory-obstruction fixture. The caller's file must remain unchanged, the manifest's retention must remain visible in the result, and the skill must not be represented as fully removed. Repeat with an ordinary regular reference file to verify that a fully removed skill still reports removal. After moving the obstruction aside and rerunning, the manifest must be removed and the caller's saved file must remain unchanged. ### Analysis or suspected cause Source-derived reasoning: `src/application/Uninstall.ts:745` retains the ownership manifest when another managed file has a retained or failed outcome. `src/application/Uninstall.ts:771` replaces the accumulated per-file outcomes with a generic success detail and omits the retained outcomes. That projection explains why a partially removed skill reports `removed`, and why a later unchanged run reports `skipped` with the same success wording. These source locators explain the observed output; no patched restoration is claimed. ### Additional context - [#2111](https://github.com/morluto/rea/issues/2111), closed as completed, concerns missing directory names for visible retained or failed skill destinations; this report loses the retention status and reason after partial managed-file removal. - [#2159](https://github.com/morluto/rea/issues/2159), closed as completed, concerns empty directory trees left after all managed files were removed; this report retains the ownership manifest and a managed reference path containing caller data. - [#3057](https://github.com/morluto/rea/issues/3057), closed as completed, concerns setup following a symlinked skill root that uninstall explicitly reports as retained; this report uses a regular skill root and hides the reference-path obstruction. - [#661](https://github.com/morluto/rea/issues/661), closed as completed, concerns per-command skills, links, and a separate manifest omitted by uninstall; this report concerns the recognized canonical skill's managed-file result. - [#2664](https://github.com/morluto/rea/issues/2664), closed as completed, concerns an owned ZCode fallback registration omitted by uninstall; this report concerns retained canonical skill-file outcomes. - [#3301](https://github.com/morluto/rea/issues/3301), open, concerns positive uninstall test fixtures with unrecognized registration commands; this reproduction needs no client registration and establishes skill ownership before partial removal.
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 17, 2026):

This no longer reproduces on current main (73965f94). It appears fixed by #3108 (cf6ea0971, fix(setup): refuse linked skill installation directories), which added a retained outcome in removeManagedSkill (src/application/Uninstall.ts).

Executed on main 73965f94 (macOS arm64, system Node 26.5.0 rather than the pinned 24.18.0; built with npm run build:cached). I ran this report's fixture through the public CLI uninstall --json, twice, in an isolated home:

  • The skill item reports status: "retained", not removed. Its detail names the obstructed path (Managed path is not a regular file: …/references/connection-and-recovery.md) and says the REA ownership manifest was retained because a managed file could not be safely removed. The "REA-managed skill files were removed" text is absent.
  • SKILL.md remains, the ordinary reference is removed, and caller.txt still contains keep this user file.
  • The second run still reports retained (not skipped).

Two related observations, not addressed here; they may need a maintainer decision:

  1. The top-level status stays complete. UninstallResult.status only admits complete | failed, so reporting partial would be a contract change.
  2. A multi-destination variant: when the shared destination is obstructed but ~/.claude/skills/reverse-engineer-anything is fully removed, the combined skill item reports removed. removeManagedSkills ranks removed above retained across destinations, while the per-destination detail still says shared (…): retained (…).

Leaving closure to a maintainer.

<!-- gh-comment-id:6232859007 --> @kaoru0822-kitauji commented on GitHub (Oct 17, 2026): This no longer reproduces on current main (73965f94). It appears fixed by #3108 (cf6ea0971, `fix(setup): refuse linked skill installation directories`), which added a `retained` outcome in `removeManagedSkill` (`src/application/Uninstall.ts`). Executed on main 73965f94 (macOS arm64, system Node 26.5.0 rather than the pinned 24.18.0; built with `npm run build:cached`). I ran this report's fixture through the public CLI `uninstall --json`, twice, in an isolated home: - The skill item reports `status: "retained"`, not `removed`. Its detail names the obstructed path (`Managed path is not a regular file: …/references/connection-and-recovery.md`) and says the REA ownership manifest was retained because a managed file could not be safely removed. The "REA-managed skill files were removed" text is absent. - `SKILL.md` remains, the ordinary reference is removed, and `caller.txt` still contains `keep this user file`. - The second run still reports `retained` (not `skipped`). Two related observations, not addressed here; they may need a maintainer decision: 1. The top-level `status` stays `complete`. `UninstallResult.status` only admits `complete | failed`, so reporting partial would be a contract change. 2. A multi-destination variant: when the shared destination is obstructed but `~/.claude/skills/reverse-engineer-anything` is fully removed, the combined `skill` item reports `removed`. `removeManagedSkills` ranks `removed` above `retained` across destinations, while the per-destination detail still says `shared (…): retained (…)`. Leaving closure to a maintainer.
Author
Owner

@coygeek commented on GitHub (Oct 18, 2026):

Addressed on main by github.com/morluto/rea@cf6ea09711. Skill uninstall now preserves retained child outcomes and their reasons, including the retained ownership manifest, instead of reporting that skill as fully removed. The existing cleanup path still preserves caller-owned obstructions. I checked the source and included retained-reference regression coverage; I have not rerun the directory-obstruction fixture. Closing as completed.

<!-- gh-comment-id:6244133986 --> @coygeek commented on GitHub (Oct 18, 2026): Addressed on main by https://github.com/morluto/rea/commit/cf6ea097116bdf3578b1e02e35c772d8aa92db00. Skill uninstall now preserves retained child outcomes and their reasons, including the retained ownership manifest, instead of reporting that skill as fully removed. The existing cleanup path still preserves caller-owned obstructions. I checked the source and included retained-reference regression coverage; I have not rerun the directory-obstruction fixture. Closing as completed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1181
No description provided.