[GH-ISSUE #4384] [Bug] Missing skill manifest hides a retained partial installation’s identity #1741

Closed
opened 2026-10-16 21:09:45 +00:00 by bsakaguchi · 2 comments
Owner

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

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

readInstalledSkillIdentity returns undefined both for an empty skill destination and for a destination with a readable retained managed reference but no SKILL.md. This was observed with an isolated production-module probe. The complete CLI diagnostic consequence is source-derived, not an executed doctor run.

Reproduction

This is an executed production-module file-inspection probe, not a compiled CLI or full doctor run. Prepare a checkout of the main source revision listed below with its pinned toolchain and npm ci. Save the following as repro.mts in a separate temporary directory:

import assert from 'node:assert/strict';
import {pathToFileURL} from 'node:url';
import {join} from 'node:path';
import {mkdir,mkdtemp,writeFile,rm,readFile} from 'node:fs/promises';
const {skillDestinations,readInstalledSkillIdentity,canonicalSkillNeedsInstall}=await import(pathToFileURL(process.env.REA_CHECKOUT+'/src/application/SetupSkill.ts').href);
const scratch=process.env.REA_SCRATCH_ROOT;
assert.ok(scratch,'Set REA_SCRATCH_ROOT to an isolated temporary directory');
await mkdir(scratch,{recursive:true});
const home=await mkdtemp(join(scratch,'synthetic-home-'));
try {
 const [destination]=skillDestinations(home,['shared'],{},'darwin');
 const absent=await readInstalledSkillIdentity(home,undefined,{},'darwin');
 assert.equal(absent,undefined);
 await mkdir(join(destination.path,'references'),{recursive:true});
 const reference=join(destination.path,'references','connection-and-recovery.md');
 await writeFile(reference,'benign retained managed-reference fixture');
 const partial=await readInstalledSkillIdentity(home,undefined,{},'darwin');
 assert.equal(partial,undefined);
 assert.equal(await canonicalSkillNeedsInstall(home,['shared'],{},'darwin'),true);
 assert.equal(await readFile(reference,'utf8'),'benign retained managed-reference fixture');
 console.log('absent skill identity: undefined');
 console.log('retained managed reference, missing manifest identity: undefined');
 console.log('canonicalSkillNeedsInstall: true; reference unchanged');
} finally {await rm(home,{recursive:true,force:true});}

Run from that checkout, substituting the temporary script location:

REA_SCRATCH_ROOT="$(mktemp -d /tmp/rea-repro.XXXXXX)" REA_CHECKOUT="$PWD" ./node_modules/.bin/tsx /tmp/rea-repro-script/repro.mts

The script uses a synthetic home, explicit Darwin destination selection and an empty override environment. It creates only a benign managed-reference fixture, reads identity/install-needed state, checks the retained bytes and removes the synthetic home. The same main checkout's TSX runner was also used with REA_CHECKOUT pointing to a separate locked-dependency checkout of the published release source. Each source revision produced the same result in both the explicit shared-selection probe and the unqualified-selection probe shown above.

Expected behavior

A destination containing a retained managed reference but no readable manifest identity should remain distinguishable from an absent skill: preserve a noncanonical identity with unavailable metadata. The repository identity invariant says unresolved identity must remain unknown rather than be treated as absent. This does not assert that the retained reference establishes complete bundle ownership or authorizes writes.

Impact and scope

The confirmed failure is loss of diagnostic distinction for partial skill installations with a missing manifest and readable retained managed files. Source inspection shows doctor consequently classifies that identity as missing. No content loss, incorrect installation authorization or missed install-needed detection was observed. No full doctor, setup, provider or real-client workflow was run. Practical population frequency is unknown.

REA version

Current main source 2c7583599b9ac1c847bd77b1940930fd8d4594c7; latest published rea-agents 6.5.0 source 3b79af28e80785afce9f5998d8f03289402a1847.

Runtime and platform

Node.js v24.18.0, macOS arm64, TSX, locked dependencies. Explicit Darwin destination selection with an empty override environment. No providers or target binary.

Target and artifact context

Synthetic shared skill destination with one readable managed reference and no manifest.

Interface

Package or setup

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Both the empty destination and the destination retaining references/connection-and-recovery.md return undefined. The observed output is:

absent skill identity: undefined
retained managed reference, missing manifest identity: undefined
canonicalSkillNeedsInstall: true; reference unchanged

The unqualified identity read reproduced once on each source baseline; a separate explicit ['shared'] identity read also reproduced once on each. Installation-needed detection remains true, and retained file bytes remain unchanged.

SetupSkill inspection and identity reader sets rootHasFiles from the retained managed reference and returns installedDestination: true with canonical: false. The same file's lines 630–650 discard that observation when manifest content is absent and there are no path issues. Doctor identity collection and state mapping passes an undefined selection for an unqualified doctor scope, uses the system host's readInstalledSkillIdentity adapter (lines 606–614), and maps an undefined identity to missing. Thus the probe uses the same identity selection as the documented doctor --skill --json path. The doctor consequence is source-derived; the complete command was not executed.

Typed outcome or recovery expected

For the same retained-reference fixture, the identity result must distinguish present noncanonical files with unknown metadata from an empty destination; the empty control must continue to return absence. Keep installation-needed detection true and retained bytes unchanged. The public doctor projection should preserve the partial-state distinction rather than report total absence. These are restoration checks, not claimed patched results.

Analysis or suspected cause

Source-derived: the second early return in the identity reader discards the inspection's installed-destination observation when manifest content is missing without a path issue. Missing-manifest tests with unreadable references take the issues-present branch and do not establish this case.

Additional context

#3308 concerns uninstall claiming a retained manifest was removed; this report concerns identity inspection when the manifest is absent. Closed #3208 and its merged fix concern unqualified selection overlooking an intact client installation; this probe uses a retained managed reference in the selected shared root and still fails after that fix. Closed #1711 concerns setup checking the wrong destination after a successful client-only installation. Merged #3108 preserves unreadable-reference issues with a missing or stale manifest; this fixture has a readable retained reference and no path issues, so it still reaches the separate manifest-absent early return. Those reports have different triggers and restoration checks.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The identity API discards observed partial-installation state, and source tracing shows an incorrect doctor diagnostic for that bounded damaged-installation state. The confirmed impact is diagnostic state loss, with no demonstrated file loss or recovery failure.
  • Workaround status: available
  • Workaround basis: The executed probe confirms installation-needed detection remains true and retained file bytes can be inspected directly; complete setup repair was not executed.
Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4384 ### 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 `readInstalledSkillIdentity` returns `undefined` both for an empty skill destination and for a destination with a readable retained managed reference but no `SKILL.md`. This was observed with an isolated production-module probe. The complete CLI diagnostic consequence is source-derived, not an executed doctor run. ### Reproduction This is an executed production-module file-inspection probe, not a compiled CLI or full doctor run. Prepare a checkout of the main source revision listed below with its pinned toolchain and `npm ci`. Save the following as `repro.mts` in a separate temporary directory: ```typescript import assert from 'node:assert/strict'; import {pathToFileURL} from 'node:url'; import {join} from 'node:path'; import {mkdir,mkdtemp,writeFile,rm,readFile} from 'node:fs/promises'; const {skillDestinations,readInstalledSkillIdentity,canonicalSkillNeedsInstall}=await import(pathToFileURL(process.env.REA_CHECKOUT+'/src/application/SetupSkill.ts').href); const scratch=process.env.REA_SCRATCH_ROOT; assert.ok(scratch,'Set REA_SCRATCH_ROOT to an isolated temporary directory'); await mkdir(scratch,{recursive:true}); const home=await mkdtemp(join(scratch,'synthetic-home-')); try { const [destination]=skillDestinations(home,['shared'],{},'darwin'); const absent=await readInstalledSkillIdentity(home,undefined,{},'darwin'); assert.equal(absent,undefined); await mkdir(join(destination.path,'references'),{recursive:true}); const reference=join(destination.path,'references','connection-and-recovery.md'); await writeFile(reference,'benign retained managed-reference fixture'); const partial=await readInstalledSkillIdentity(home,undefined,{},'darwin'); assert.equal(partial,undefined); assert.equal(await canonicalSkillNeedsInstall(home,['shared'],{},'darwin'),true); assert.equal(await readFile(reference,'utf8'),'benign retained managed-reference fixture'); console.log('absent skill identity: undefined'); console.log('retained managed reference, missing manifest identity: undefined'); console.log('canonicalSkillNeedsInstall: true; reference unchanged'); } finally {await rm(home,{recursive:true,force:true});} ``` Run from that checkout, substituting the temporary script location: ```sh REA_SCRATCH_ROOT="$(mktemp -d /tmp/rea-repro.XXXXXX)" REA_CHECKOUT="$PWD" ./node_modules/.bin/tsx /tmp/rea-repro-script/repro.mts ``` The script uses a synthetic home, explicit Darwin destination selection and an empty override environment. It creates only a benign managed-reference fixture, reads identity/install-needed state, checks the retained bytes and removes the synthetic home. The same main checkout's TSX runner was also used with `REA_CHECKOUT` pointing to a separate locked-dependency checkout of the published release source. Each source revision produced the same result in both the explicit shared-selection probe and the unqualified-selection probe shown above. ### Expected behavior A destination containing a retained managed reference but no readable manifest identity should remain distinguishable from an absent skill: preserve a noncanonical identity with unavailable metadata. The [repository identity invariant](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/AGENTS.md#L19-L21) says unresolved identity must remain unknown rather than be treated as absent. This does not assert that the retained reference establishes complete bundle ownership or authorizes writes. ### Impact and scope The confirmed failure is loss of diagnostic distinction for partial skill installations with a missing manifest and readable retained managed files. Source inspection shows doctor consequently classifies that identity as `missing`. No content loss, incorrect installation authorization or missed install-needed detection was observed. No full doctor, setup, provider or real-client workflow was run. Practical population frequency is unknown. ### REA version Current main source `2c7583599b9ac1c847bd77b1940930fd8d4594c7`; latest published `rea-agents` 6.5.0 source `3b79af28e80785afce9f5998d8f03289402a1847`. ### Runtime and platform Node.js v24.18.0, macOS arm64, TSX, locked dependencies. Explicit Darwin destination selection with an empty override environment. No providers or target binary. ### Target and artifact context Synthetic shared skill destination with one readable managed reference and no manifest. ### Interface Package or setup ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Both the empty destination and the destination retaining `references/connection-and-recovery.md` return `undefined`. The observed output is: ```text absent skill identity: undefined retained managed reference, missing manifest identity: undefined canonicalSkillNeedsInstall: true; reference unchanged ``` The unqualified identity read reproduced once on each source baseline; a separate explicit `['shared']` identity read also reproduced once on each. Installation-needed detection remains true, and retained file bytes remain unchanged. [SetupSkill inspection and identity reader](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/src/application/SetupSkill.ts#L373-L460) sets `rootHasFiles` from the retained managed reference and returns `installedDestination: true` with `canonical: false`. The same file's lines 630–650 discard that observation when manifest content is absent and there are no path issues. [Doctor identity collection and state mapping](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/src/application/Doctor.ts#L314-L386) passes an undefined selection for an unqualified doctor scope, uses the system host's `readInstalledSkillIdentity` adapter (lines 606–614), and maps an undefined identity to `missing`. Thus the probe uses the same identity selection as the documented `doctor --skill --json` path. The doctor consequence is source-derived; the complete command was not executed. ### Typed outcome or recovery expected For the same retained-reference fixture, the identity result must distinguish present noncanonical files with unknown metadata from an empty destination; the empty control must continue to return absence. Keep installation-needed detection true and retained bytes unchanged. The public doctor projection should preserve the partial-state distinction rather than report total absence. These are restoration checks, not claimed patched results. ### Analysis or suspected cause Source-derived: the second early return in the identity reader discards the inspection's installed-destination observation when manifest content is missing without a path issue. Missing-manifest tests with unreadable references take the issues-present branch and do not establish this case. ### Additional context [#3308](https://github.com/morluto/rea/issues/3308) concerns uninstall claiming a retained manifest was removed; this report concerns identity inspection when the manifest is absent. Closed [#3208](https://github.com/morluto/rea/issues/3208) and its merged fix concern unqualified selection overlooking an intact client installation; this probe uses a retained managed reference in the selected shared root and still fails after that fix. Closed [#1711](https://github.com/morluto/rea/issues/1711) concerns setup checking the wrong destination after a successful client-only installation. Merged [#3108](https://github.com/morluto/rea/pull/3108) preserves unreadable-reference issues with a missing or stale manifest; this fixture has a readable retained reference and no path issues, so it still reaches the separate manifest-absent early return. Those reports have different triggers and restoration checks. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The identity API discards observed partial-installation state, and source tracing shows an incorrect doctor diagnostic for that bounded damaged-installation state. The confirmed impact is diagnostic state loss, with no demonstrated file loss or recovery failure. - Workaround status: available - Workaround basis: The executed probe confirms installation-needed detection remains true and retained file bytes can be inspected directly; complete setup repair was not executed.
Author
Owner

@jayinvers commented on GitHub (Oct 16, 2026):

Confirmation from main 60bf1024 with this report's fixture (executed source-level probe, synthetic home, selected shared destination, retained references/connection-and-recovery.md and no SKILL.md):

  • 9abdad33 resolves the reported identity loss: the partial destination now returns {canonical: false, version: null, toolCount: null} instead of undefined.
  • The empty-destination control still returns undefined; canonicalSkillNeedsInstall stays true; retained bytes are unchanged.

One nuance remains at the maintainer's discretion: doctor renders that case as stale with "Installed REA skill identity is stale." (anchored by the acceptance test in 9abdad33), so the unknown metadata is presented as a known stale version rather than its own state. If a distinct rendering is ever wanted, jayinvers/rea:fix/skill-partial-identity implements a manifestMissing marker plus a partial state with the detail "Installed REA skill files are present without a readable skill manifest." — it was withdrawn as PR #4492 to respect the anchored contract, and the branch stays available.

Either way, the core of this report looks resolved on main. Thanks!

<!-- gh-comment-id:6221861608 --> @jayinvers commented on GitHub (Oct 16, 2026): Confirmation from main `60bf1024` with this report's fixture (executed source-level probe, synthetic home, selected shared destination, retained `references/connection-and-recovery.md` and no `SKILL.md`): - 9abdad33 resolves the reported identity loss: the partial destination now returns `{canonical: false, version: null, toolCount: null}` instead of `undefined`. - The empty-destination control still returns `undefined`; `canonicalSkillNeedsInstall` stays `true`; retained bytes are unchanged. One nuance remains at the maintainer's discretion: doctor renders that case as `stale` with "Installed REA skill identity is stale." (anchored by the acceptance test in 9abdad33), so the unknown metadata is presented as a known stale version rather than its own state. If a distinct rendering is ever wanted, `jayinvers/rea:fix/skill-partial-identity` implements a `manifestMissing` marker plus a `partial` state with the detail "Installed REA skill files are present without a readable skill manifest." — it was withdrawn as PR #4492 to respect the anchored contract, and the branch stays available. Either way, the core of this report looks resolved on main. Thanks!
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Revalidated on current main d818c3d2639d3115edb8a5269a754101aea6c3bd. Published fix 9abdad335 removed the manifest-absent early return; readInstalledSkillIdentity now returns absence only when inspection confirms no installed destination. Retained managed files without a manifest remain noncanonical with null version/tool-count metadata.

Current production-boundary and public CLI controls pass: the unqualified and selected-client doctor commands report the partial destination as stale, preserve its reference bytes, and still distinguish a genuinely empty destination as missing. Installation-needed detection and cleanup ownership checks remain intact. Executed the five-file header/skill/doctor lifecycle selection (42 tests), plus four Ghidra/doctor consumer files (144 tests); no source changes were needed for this issue.

This is controlled filesystem/public CLI verification on macOS arm64 / Node24.18.0, not verification of real client integrations or native Windows/Linux behavior. Closing the original diagnostic identity-loss report as fixed.

<!-- gh-comment-id:6242929227 --> @morluto commented on GitHub (Oct 17, 2026): Revalidated on current main `d818c3d2639d3115edb8a5269a754101aea6c3bd`. Published fix `9abdad335` removed the manifest-absent early return; `readInstalledSkillIdentity` now returns absence only when inspection confirms no installed destination. Retained managed files without a manifest remain noncanonical with null version/tool-count metadata. Current production-boundary and public CLI controls pass: the unqualified and selected-client doctor commands report the partial destination as stale, preserve its reference bytes, and still distinguish a genuinely empty destination as missing. Installation-needed detection and cleanup ownership checks remain intact. Executed the five-file header/skill/doctor lifecycle selection (42 tests), plus four Ghidra/doctor consumer files (144 tests); no source changes were needed for this issue. This is controlled filesystem/public CLI verification on macOS arm64 / Node24.18.0, not verification of real client integrations or native Windows/Linux behavior. Closing the original diagnostic identity-loss report as fixed.
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#1741
No description provided.