-
Notifications
You must be signed in to change notification settings - Fork 0
fix(lifecycle): fail closed on inconclusive npm ghost probes #399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+318
−6
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
2 changes: 2 additions & 0 deletions
2
openspec/changes/fix-npm-ghost-probe-false-positive/.openspec.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| schema: spec-driven | ||
| created: 2026-07-01 |
30 changes: 30 additions & 0 deletions
30
openspec/changes/fix-npm-ghost-probe-false-positive/design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| ## Context | ||
|
|
||
| `isManagedPackageAbsent()` delegates to installer `getInstalledVersion` hooks. npm's implementation runs `npm list -g --depth=0 --json` and returns `undefined` whenever the command exits non-zero, even when stdout contains valid JSON showing the target package is still installed. | ||
|
|
||
| ## Goals / Non-Goals | ||
|
|
||
| **Goals** | ||
|
|
||
| - Confirm npm package absence only when a scoped probe parses valid JSON and the package is missing. | ||
| - Fail closed on inconclusive npm probes during ghost recovery. | ||
| - Keep version probing behavior stable for callers that only need a version string. | ||
|
|
||
| **Non-Goals** | ||
|
|
||
| - Rework Windows deferred binary self-upgrade semantics. | ||
| - Add doctor-only repair flows. | ||
| - Change bun or other installer ghost recovery beyond existing behavior. | ||
|
|
||
| ## Decisions | ||
|
|
||
| - Add an npm `probePackagePresence` helper returning `present`, `absent`, or `unknown`. | ||
| - Use scoped `npm list -g <package> --depth=0 --json` and parse stdout regardless of exit code when JSON is valid. | ||
| - Treat a matching dependency key as present even when its version is unreadable, and treat structured npm error payloads as unknown. | ||
| - Extend the managed installer interface with optional `probePackagePresence` and use it in `isManagedPackageAbsent()` when available. | ||
| - Fall back to the existing `getInstalledVersion === undefined` check for installers without the new hook. | ||
|
|
||
| ## Risks / Trade-offs | ||
|
|
||
| - [Risk] Scoped npm list still returns inconclusive output on some broken npm installs. → Mitigation: return `unknown` and skip ghost recovery. | ||
| - [Risk] Extra probe hook adds interface surface. → Mitigation: optional hook used only for absence confirmation. |
24 changes: 24 additions & 0 deletions
24
openspec/changes/fix-npm-ghost-probe-false-positive/proposal.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| ## Why | ||
|
|
||
| The recent ghost-state uninstall recovery path treats npm `getInstalledVersion` returning `undefined` as confirmed package absence. npm's global `list` probe returns `undefined` on any non-zero exit code without parsing stdout, so a broken global dependency tree or probe failure can clear tracked state while the agent package remains installed. | ||
|
|
||
| ## What Changes | ||
|
|
||
| - Harden npm managed package presence probing for ghost uninstall recovery. | ||
| - Distinguish confirmed absence from inconclusive probe results before clearing state. | ||
| - Add regression tests for npm probe failure and confirmed-absence paths. | ||
|
|
||
| ## Capabilities | ||
|
|
||
| ### New Capabilities | ||
|
|
||
| - None. | ||
|
|
||
| ### Modified Capabilities | ||
|
|
||
| - `agent-update`: ghost uninstall recovery must not clear state when npm presence probing is inconclusive. | ||
|
|
||
| ## Impact | ||
|
|
||
| - Affected code: `src/package-manager/npm.ts`, `src/package-manager/installers.ts`, `src/package-manager/index.ts`, related tests. | ||
| - No CLI flags, schema version, or command catalog changes. |
34 changes: 34 additions & 0 deletions
34
openspec/changes/fix-npm-ghost-probe-false-positive/specs/agent-update/spec.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| ## MODIFIED Requirements | ||
|
|
||
| ### Requirement: Uninstall MUST recover ghost managed install state | ||
|
|
||
| When a managed package is no longer installed but Quantex still records install state, uninstall SHALL clear the stale state entry after confirming package absence through the recorded installer. | ||
|
|
||
| #### Scenario: Retrying uninstall after package removal succeeded but state persistence failed | ||
|
|
||
| - **GIVEN** an agent has recorded managed install state | ||
| - **AND** the managed package is no longer installed on the system | ||
| - **AND** the recorded package manager is available and can confirm absence | ||
| - **WHEN** the user runs `quantex uninstall <agent>` | ||
| - **AND** the managed package-manager uninstall command reports failure | ||
| - **THEN** Quantex removes the installed-agent state entry | ||
| - **AND** the uninstall command reports success | ||
|
|
||
| #### Scenario: Ghost recovery does not run when the package manager is unavailable | ||
|
|
||
| - **GIVEN** an agent has recorded managed install state | ||
| - **AND** the recorded package manager is unavailable | ||
| - **WHEN** the user runs `quantex uninstall <agent>` | ||
| - **AND** the managed package-manager uninstall command reports failure | ||
| - **THEN** Quantex does not remove the installed-agent state entry | ||
| - **AND** the uninstall command reports failure | ||
|
|
||
| #### Scenario: Ghost recovery does not run when npm presence probing is inconclusive | ||
|
|
||
| - **GIVEN** an agent has recorded managed install state with install type `npm` | ||
| - **AND** npm is available | ||
| - **AND** npm global presence probing cannot confirm whether the package is installed or absent | ||
| - **WHEN** the user runs `quantex uninstall <agent>` | ||
| - **AND** the managed package-manager uninstall command reports failure | ||
| - **THEN** Quantex does not remove the installed-agent state entry | ||
| - **AND** the uninstall command reports failure | ||
10 changes: 10 additions & 0 deletions
10
openspec/changes/fix-npm-ghost-probe-false-positive/tasks.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| ## 1. Implementation | ||
|
|
||
| - [x] 1.1 Add npm scoped presence probing with `present` / `absent` / `unknown` outcomes | ||
| - [x] 1.2 Wire optional installer `probePackagePresence` into ghost recovery | ||
| - [x] 1.3 Add regression tests for npm inconclusive probe and confirmed absence paths | ||
| - [x] 1.4 Fail closed for structured npm errors and package entries with unreadable versions | ||
|
|
||
| ## 2. Validation | ||
|
|
||
| - [x] 2.1 Run `bun run lint`, `bun run format:check`, `bun run typecheck`, and `bun run test` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking (archive follow-up): this delta re-lists the existing ghost-recovery requirement. Archive agent should merge additively — add only the npm inconclusive-probe scenario to
openspec/specs/agent-update/spec.md, without duplicating or dropping prior scenarios.