Skip to content

feat(install): receipt-owned uninstall lifecycle, format/2 receipts, and doctor activation states (#101) - #452

Merged
ScriptedAlchemy merged 7 commits into
mainfrom
feat/101-managed-lifecycle
Sep 4, 2026
Merged

feat(install): receipt-owned uninstall lifecycle, format/2 receipts, and doctor activation states (#101)#452
ScriptedAlchemy merged 7 commits into
mainfrom
feat/101-managed-lifecycle

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #101 (managed lifecycle for generated host bundles).

Deferral reversal, stated plainly: the maintainer's earlier decision kept the mutation half of #101 behind the G4 gate ("doctor's read-only suggestions are the supported surface for now"; read-only Doctor landed in #236). On 2026-09-03 the maintainer asked that every open agent-bundle issue be fixed, which reverses that deferral. This PR therefore ships the mutation half — but every mutation is strictly opt-in (a separate uninstall verb; nothing runs on install or doctor), receipt-owned (only what the receipt records is ever removed), and fail-closed (missing receipt, mismatch, foreign, unconfirmed purge, unusable host inventory all refuse before any change). Workbench exposure stays out per G6.

Builds on #420 (receipts + --replace) and #414 (Cursor --mode marketplace, hooks doctor findings).

What lands

  1. agent-bundle uninstall <host> [--from] [--scope] [--mode local|marketplace] [--keep-data | --purge-data --confirm-purge] [--force] [--plan] [--json], the package-relative installer bin's uninstall <host>, and the emitted install.mjs --uninstall.
    • Cursor local: removes the receipt's files, prunes its installer-created directories and the plugin root when empty, then the ~/.cursor/plugins[/local] directories the install itself created (hostDirectories). Unowned entries are retained and listed; a remnant receipt (owns no files) is written when the root survives so a later purge can still prune and Doctor can explain the dir.
    • Cursor marketplace: verifies staged HEAD == recorded commit, removes the repo + store receipt; an imported copy is Cursor-owned → manual + Customize step.
    • Claude/Codex: plugin list --json (unusable → AB7004 fail-closed), copy-vs-receipt check, claude plugin uninstall <id> --scope <s> --keep-data / codex plugin remove <id>, then plugin marketplace remove unless another installed plugin still uses the marketplace (retained), then the store receipt. Orphaned receipt (host already forgot) → already-absent, no host verb.
    • Data: state/ kept by default; --purge-data requires --confirm-purge (AB7008). Typed per-host data.outcome: kept/purged/absent (Cursor), retained-by-host (Claude orphans the cache copy for its grace period), removed-by-host/unavailable (Codex deletes the cache and has no keep-data option — stated honestly, not faked).
    • Refusals: AB7007 content/version/HEAD mismatch or foreign/symlink (foreign and other-plugin receipts refused even with --force), AB7008 purge confirmation, AB7009 missing receipt. (AB7006 was taken by fix(install): surface claude plugin list --json errors in doctor/install (AB7325, AB7006); reject the auto-loaded hooks path in the Claude manifest schema #479 on main — Claude load errors — so the uninstall refusals are AB7007AB7009.)
    • --plan produces the identical report (exact absolute paths, host verbs) with no writer; rerun after uninstall is not-installed.
  2. Trust/activation states in doctor --from: lifecycle = placed → registered → enabled → active, each observed (with evidence) or typed unavailable (with reason). Claude/Codex from plugin list --json rows and enabled flags; Cursor local dir = registration; Cursor marketplace import via .cache-complete; enabled/active on Cursor and active on Claude/Codex typed unavailable (server-assigned state.vscdb ids, enable_cc_plugin_import gating, no live-session verb). New AB7330 (lifecycle stage; renumbered twice as main took AB7325AB7327 in fix(install): surface claude plugin list --json errors in doctor/install (AB7325, AB7006); reject the auto-loaded hooks path in the Claude manifest schema #479, feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7326 (#426) #482, fix(build,validate,doctor): run the Claude host validator and --plugin-dir load check; doctor reads enabled (AB7327) (#475, #476) #504). Capability tables gain dated lifecycle rows (uninstall, keepData, activationState) per host; all five adapter revisions bumped.
  3. Receipts as single source of truth: format agent-bundle-install-receipt/2 adds mode, scope, registrations, hostDirectories, updatedAt. Claude/Codex/Cursor-marketplace installs write store receipts under <host root>/agent-bundle/receipts/<plugin>.<marketplace>.<scope>[.<project digest>].json (Cursor staging: <plugin>.marketplace.json; files: []). install --replace, uninstall, and doctor all consume them; Doctor inventories the store and flags orphaned receipts (AB7328). Migration: format/1 receipts read with synthesized fields + migratedFrom, diagnosed AB7329, rewritten as format/2 by an identical install rerun; malformed current-format receipts read as absent (never crash).
  4. Safety proofs: host-install proofs install → --plan (no-op) → uninstall → snapshot diff: byte-identical isolated home for Cursor local and portable (install.mjs --uninstall); zero Agent Bundle residue + every remaining entry classified as host-owned bookkeeping for Claude (.orphaned_at cache copy, empty registries, settings, session bookkeeping) and Codex (empty config.toml, empty dirs); --keep-data preserves state; missing-receipt / mismatch / unconfirmed-purge refusals; packed-tarball variant through the package bin. Doctor and unit tests for every path.

Docs: docs/diagnostics.md (new sections; family table; receipt v2), docs/framework-mode.md, package README, INSTALL.md template (per-host "Uninstall" section), website en+zh cli.mdx and installation.mdx. Changeset: agent-bundle minor.

Acceptance items (#101)

Item Status
Dry run opens no writer and reports exact planned paths and shared-config mutations --plan; proofs assert snapshot unchanged
Install and update preserve unrelated host configuration ✅ owned-files-only replace (#420) + proofs: pre-existing entries never removed/changed
Interrupted multi-component install recovers or rolls back safely ✅ staged copy + atomic rename, receipt lands last (#420); uninstall removes the receipt last so a failed run is re-runnable; Doctor AB7305 for leftovers. No journal file — recovery is by rerun.
Repair restores owned artifacts without adopting foreign paths ✅ same-version stale receipt-owned copy replaced automatically; foreign always refused
Uninstall removes only receipt-owned artifacts and registrations ✅ this PR
Doctor distinguishes activation deferred from installed-and-active lifecycle stages with typed unavailable
≥2 materially different host registration mechanisms pass real lifecycle tests ✅ Claude (marketplace + plugin CLI), Codex (CLI + config.toml), Cursor (directory placement, marketplace staging) — real proofs

Evidence

Local, this worktree, after rebase on origin/main:

  • pnpm typecheck ✅ · pnpm lint ✅ (0 errors)
  • pnpm test:unit ✅ 3043 passed / 0 failed / 5 skipped (head a9e1f4dbe, rebased on 284141958); after the c6db0ff rebase: typecheck ✅, lint ✅, install/uninstall/doctor/install-surface/installer-entry suites ✅ (135 passed), docs site build ✅
  • pnpm test:route-unit ✅ 44 · pnpm test:projection ✅ 145
  • pnpm build && pnpm test:integration:run ✅ 978 passed; the single failure (examples-real.e2eDevLockError, pid of a concurrent lane's dev server) passed 5/5 on isolated rerun
  • pnpm test:host-install:build ✅ 23 passed / 2 skipped (opt-in Claude session) — includes the 4 new uninstall proofs against real claude 2.1.257 and codex 0.147.0 in isolated HOMEs
  • pnpm test:host-install:packed:build ✅ 6 passed (3 new packed uninstall proofs)
  • pnpm docs:site:build ✅ (language parity ok)

Real-host observations recorded in the capability tables (2026-09-03): Claude plugin uninstall --keep-data orphans the cache copy (.orphaned_at), leaves empty installed_plugins.json/known_marketplaces.json/settings.json; marketplace remove of an unknown marketplace exits 1. Codex plugin remove deletes the cache, marketplace remove leaves an empty config.toml; plugin remove of an unknown plugin exits 0.

Test plan

  • unit: tests/uninstall.test.ts (Cursor local/marketplace, Claude/Codex mocked runners, CLI, format), tests/install.test.ts (store receipts, v1 migration reader), tests/install-surface.test.ts (install.mjs --uninstall), tests/installer-entry.test.ts (bin uninstall), tests/doctor.test.ts (AB7328/AB7329/AB7330, remnant)
  • host-install proofs + packed proofs (above)
  • docs site build

Review status

  • Round 1 (chatgpt-codex-connector, 3 threads): marketplace retention at other scopes, retention when inventory fails, doctor receipt inventory with unavailable probe — fixed, resolved.
  • Round 2 + 3 (10 threads): shared-scope purge refusal (AB7008), pre-existing marketplace never claimed, imported-copy detection after staging is gone, exact --plan prune simulation, patch changeset, remnant rerun as not-installed no-op, project-keyed Claude receipts, state-only remnant check in doctor, purged directories reported as directories — fixed in 1da381c, replied and resolved. The (#452) attribution thread was answered (AGENTS.md: summary ends with (#PR)).
  • Round 4 (2 threads on 1da381c): plugin-only stored receipts now count as marketplace dependents and same-plugin receipts block --purge-data (AB7008); Doctor cross-checks Claude project/local receipts from their recorded projectRoot (unknown, never orphaned, when the root cannot be listed) — fixed in a9e1f4dbe, replied and resolved.
  • Round 4 review of the rebased head 0226dd3 (2 threads): surviving unowned directories listed as name/ under retained (core + install.mjs, plan and result); doctor --from checks the remnant directory's contents before calling it state-only and names retained extras in AB7307 — fixed in 3ae5ca9, replied and resolved.
  • Round 5 (4 threads on 3ae5ca9): host-CLI receipts keyed by marketplace (<plugin>.<marketplace>.<scope>); the marketplace ownership claim moves to a dependent's store receipt when the consumed receipt recorded it (or the detail states the marketplace now counts as user-owned); --plan lists the receipt-store directories the run would prune; an explicit --purge-data --confirm-purge consumes an empty remnant — fixed in 5b2c4fe.
  • Rebase onto main @ c6db0ff (chore(ci): remove the release audit gate #487): fix(install): surface claude plugin list --json errors in doctor/install (AB7325, AB7006); reject the auto-loaded hooks path in the Claude manifest schema #479 landed AB7006/AB7325 for Claude load errors, so this PR's codes moved to AB7009 (missing receipt) and AB7328 (lifecycle stage) — code, tests, docs (en+zh), changeset. The Claude store receipt is now written before fix(install): surface claude plugin list --json errors in doctor/install (AB7325, AB7006); reject the auto-loaded hooks path in the Claude manifest schema #479's post-install load verdict, so a refused copy stays receipt-owned for uninstall. fix(install): surface claude plugin list --json errors in doctor/install (AB7325, AB7006); reject the auto-loaded hooks path in the Claude manifest schema #479's install.test.ts load-error test now pins its host roots inside the fixture (it wrote a receipt to the real ~/.claude otherwise). Branch history squashed to one feature commit plus the rebase/round-5 fixes.
  • Round 5 threads replied and resolved; @codex review requested on 010e1d7 (squashed head). Merge gate: CI green on that head with every thread addressed.
  • CI note: Release gates failed three times on 0226dd3/1da381c inside scripts/audit-packed-release.mjsnpm audit (registry 400/503 from registry.npmjs.org/-/npm/v1/security/audits/quick, once a hang); attempt 3 on 1da381c passed. main was red on the same job in the same window. Not a branch defect.
  • Rebases onto main through feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7326 (#426) #482/fix(build,validate,doctor): run the Claude host validator and --plugin-dir load check; doctor reads enabled (AB7327) (#475, #476) #504/docs(site): drop the duplicate lineage Detail header key (#502 + #504 collision) #507 (Cursor placeholder expansion, AB7325AB7327 in use): this PR's doctor codes moved once more to AB7328 (orphaned/unreadable store receipt), AB7329 (marketplace receipt without registration), AB7330 (uninstall not exposed); AB7009 unchanged. Docs (en+zh), README, docs/diagnostics.md, changeset follow.
  • Round 6 (3 threads on the rebased head, addressed by code only — no review re-requests are posted from here on): Claude, Cursor, and portable adapterRevision advanced to 1.27.0 / 1.13.0 / 1.10.0 (f74f362); a written Cursor PLUGIN_DATA directory is receipt-owned durable state — kept behind a remnant receipt carrying cursorExpansion, removed by --purge-data --confirm-purge, an empty installer-created one pruned with its agent-bundle/plugin-data parents, a recorded path outside this home's plugin-data never touched, Doctor's AB7307 names it (96d31e0); Claude's cross-project plugins/installed_plugins.json registry is read alongside plugin list --json and the receipt store, so hand-made project/local installs elsewhere retain the marketplace and block a shared purge (AB7008), with an unreadable registry failing closed (d7068b7); INSTALL.md / README / site (en+zh) / changeset updated (9e69e8a).
  • Merge gate: all checks green on the current head with every review thread addressed in code.
  • Unreviewed SHAs (no Codex response to the @codex review requests at 02:00Z, 02:28Z, and 03:01Z UTC after ~35 min; merged on CI green per lane rules): d795dac8f, 9e5c95242, 2f9541b6c, f74f3621c, 96d31e07b, d7068b743, 9e69e8ac9 (the rebased branch; every earlier Codex thread from rounds 1–6 is addressed in these commits).

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9e69e8a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T03:06:02.729271Z 96d31e0 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@452
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@452
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@452

commit: 9e69e8a

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8338830fbc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cad3476cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts
Comment thread packages/agent-bundle/src/install/install.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread .changeset/101-managed-lifecycle.md Outdated
Comment thread .changeset/101-managed-lifecycle.md Outdated
Comment thread packages/agent-bundle/README.md
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…tory receipts without a host probe

Codex review on #452:
- uninstall: a same-id Claude row at another scope is a marketplace dependent
  (marketplace remove applies to every scope); a failed dependency re-read of
  plugin list --json is 'unknown' and retains the marketplace instead of
  reading as an empty inventory.
- doctor: the receipt store is inventoried from disk even when the host
  executable cannot be probed; the host cross-check alone decides
  consistent/orphaned, otherwise unknown.
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5bb7d521be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/install.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…tory receipts without a host probe

Codex review on #452:
- uninstall: a same-id Claude row at another scope is a marketplace dependent
  (marketplace remove applies to every scope); a failed dependency re-read of
  plugin list --json is 'unknown' and retains the marketplace instead of
  reading as an empty inventory.
- doctor: the receipt store is inventoried from disk even when the host
  executable cannot be probed; the host cross-check alone decides
  consistent/orphaned, otherwise unknown.
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…, unconditional receipt inventory

Address codex review on #452:
- install claims a Claude/Codex marketplace only when it was absent before
  the install (or a prior receipt already claimed it); pre-existing or
  unreadable marketplaces are never recorded as owned
- uninstall scans live host inventory plus every stored receipt for
  marketplace dependents and retains the marketplace when any exist or
  the inventory is unknown; Claude --purge-data refuses (AB7008) when the
  same plugin is installed at another scope or dependents are unknown
- Claude project/local receipts are keyed by project root so parallel
  projects no longer overwrite each other's receipt
- doctor inventories store receipts even when the host probe is unavailable
- uninstall --plan for Cursor local simulates the prune precisely;
  --keep-data reruns on a remnant are a not-installed no-op
- Cursor marketplace imported detection uses the receipt commit even after
  the staged repo is gone
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/101-managed-lifecycle branch from 5bb7d52 to 1da381c Compare September 3, 2026 22:39
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1da381c146

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…ross-check scoped receipts from their project root

Review round 4 on #452:

- `uninstall`: a stored receipt whose plugin installs from the marketplace
  counts as a dependent even when it records only its plugin registration
  (an install made after the marketplace already existed), so the
  marketplace is retained; a stored receipt for the same plugin id at
  another scope or project also blocks `--purge-data` (AB7008) because the
  cache copy and `plugins/data/<id>` are scope-less.
- `doctor`: a Claude `project`/`local` receipt is cross-checked against
  `plugin list --json` run from its recorded `projectRoot` (once per root),
  and is `unknown` — never `orphaned` (AB7326) — when that root cannot be
  listed or the receipt records none.

Tests in uninstall.test.ts and doctor.test.ts; docs/diagnostics.md states both rules.
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…tory receipts without a host probe

Codex review on #452:
- uninstall: a same-id Claude row at another scope is a marketplace dependent
  (marketplace remove applies to every scope); a failed dependency re-read of
  plugin list --json is 'unknown' and retains the marketplace instead of
  reading as an empty inventory.
- doctor: the receipt store is inventoried from disk even when the host
  executable cannot be probed; the host cross-check alone decides
  consistent/orphaned, otherwise unknown.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/101-managed-lifecycle branch from a9e1f4d to 0226dd3 Compare September 3, 2026 23:43
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…, unconditional receipt inventory

Address codex review on #452:
- install claims a Claude/Codex marketplace only when it was absent before
  the install (or a prior receipt already claimed it); pre-existing or
  unreadable marketplaces are never recorded as owned
- uninstall scans live host inventory plus every stored receipt for
  marketplace dependents and retains the marketplace when any exist or
  the inventory is unknown; Claude --purge-data refuses (AB7008) when the
  same plugin is installed at another scope or dependents are unknown
- Claude project/local receipts are keyed by project root so parallel
  projects no longer overwrite each other's receipt
- doctor inventories store receipts even when the host probe is unavailable
- uninstall --plan for Cursor local simulates the prune precisely;
  --keep-data reruns on a remnant are a not-installed no-op
- Cursor marketplace imported detection uses the receipt commit even after
  the staged repo is gone
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
…ross-check scoped receipts from their project root

Review round 4 on #452:

- `uninstall`: a stored receipt whose plugin installs from the marketplace
  counts as a dependent even when it records only its plugin registration
  (an install made after the marketplace already existed), so the
  marketplace is retained; a stored receipt for the same plugin id at
  another scope or project also blocks `--purge-data` (AB7008) because the
  cache copy and `plugins/data/<id>` are scope-less.
- `doctor`: a Claude `project`/`local` receipt is cross-checked against
  `plugin list --json` run from its recorded `projectRoot` (once per root),
  and is `unknown` — never `orphaned` (AB7326) — when that root cannot be
  listed or the receipt records none.

Tests in uninstall.test.ts and doctor.test.ts; docs/diagnostics.md states both rules.
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0226dd35c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…checks remnant contents on the bundle path

Review round 5 on #452:

- uninstall (core + emitted install.mjs): an unowned directory that holds
  nothing retained survives the prune (only owned directories are
  candidates), so --plan and the completed result now list it as
  `name/` under retained instead of leaving it unreported.
- doctor --from: the exact-bundle branch reads the Cursor directory before
  calling a remnant state-only, like the inventory branch already did; a
  remnant receipt beside unowned entries names those entries in AB7307.

Tests in uninstall.test.ts, install-surface.test.ts, doctor.test.ts;
docs/diagnostics.md states both rules.
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ae5ca9f2e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/install.ts
Comment thread packages/agent-bundle/src/install/uninstall.ts
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…and doctor activation states (#101)

Squash of the #452 branch (rounds 1-5 of review folded in) ahead of the
rebase onto main, which now carries #479's AB7006/AB7325.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/101-managed-lifecycle branch from 3ae5ca9 to cbc13d9 Compare September 4, 2026 00:55
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…ace ownership claim to a dependent receipt; plan store pruning; purge consumes an empty remnant

Review round 6 on #452:
- publicHostReceiptPath: <plugin>.<marketplace>.<scope>[.<digest>].json —
  the same plugin from two marketplaces is two installs with two receipts.
- uninstall (Claude/Codex): when the consumed receipt is the one recording
  the marketplace registration and dependents keep the marketplace, the
  claim moves to the first dependent store receipt not already recording
  it; with no dependent receipt the detail says the marketplace now counts
  as user-owned.
- --plan lists the receipt store directories the run would prune
  (simulateRemoveStoredInstallReceipt; Cursor marketplace staging likewise).
- Cursor local: an explicit --purge-data --confirm-purge consumes a remnant
  receipt even when state/ was removed by hand (emitted uninstaller too).
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 010e1d79c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/uninstall.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2c884c137

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/install.ts Outdated
Comment thread packages/agent-bundle/src/install/surface.ts Outdated
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 939683025c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/agent-bundle/src/install/uninstall.ts
Comment thread packages/agent-bundle/src/install/install.ts Outdated
Comment thread packages/agent-bundle/src/install/doctor.ts Outdated
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…and doctor activation states (#101)

Squash of the #452 branch (review rounds 1-7 folded in) ahead of the
rebase onto main, which now carries #482's AB7326.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/101-managed-lifecycle branch from 9396830 to 6d2acce Compare September 4, 2026 02:16
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 348cc00496

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const others: string[] = [];
const sameOtherScopes: string[] = [];
try {
const result = await runner.run(host, ['plugin', 'list', '--json'], { cwd: identity.bundleRoot });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retain marketplaces with unenumerated Claude project dependents

When an Agent Bundle-owned marketplace is also used by a manually installed plugin in another Claude project, this listing runs only from the current bundle root, so the other project's cwd-relative registration is invisible; because a manual install has no Agent Bundle receipt, the store scan cannot recover it either. The function then reports no dependents and removes the marketplace globally, even though the pinned evidence says project/local registrations live in project settings and marketplace removal applies to every scope (claude-2.1.250.json:22,511). Unlike the already-fixed receipted other-project case, retain the marketplace when unreceipted project consumers cannot be enumerated.

Useful? React with 👍 / 👎.

Comment on lines +374 to +375
' const dataPaths = stateMetadata === undefined ? [] : [stateDirectory];',
" const dataOutcome = dataPaths.length === 0 ? 'absent' : purgeData ? 'purged' : 'kept';",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track and purge the standalone PLUGIN_DATA directory

For an Agent Plugins pack containing a stdio server, the emitted installer creates ~/.cursor/agent-bundle/plugin-data/<plugin> and records it as receipt.cursorExpansion.pluginData, but the uninstall path considers only <destination>/state to be durable data. Consequently --purge-data --confirm-purge reports purged while leaving the PLUGIN_DATA directory and all of its contents behind; consuming the receipt also discards the only recorded path. Include the recorded PLUGIN_DATA directory in the purge/report and preserve enough ownership information for later remnant cleanup.

Useful? React with 👍 / 👎.

Comment on lines +15 to +16
"lifecycle": {
"uninstall": {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Advance every changed adapter revision

This adds a lifecycle contract to the Claude capability table, but Claude's adapterRevision remains 1.26.0; Cursor and portable likewise keep their parent revisions even though their capability tables and generated install surfaces change, while only Codex advances. Since repository-owned capability tables are intentionally represented by the monotonic adapter revision rather than hashed (packages/agent-bundle/README.md:1003-1006), consumers cannot distinguish artifacts using these new contracts from the preceding adapter versions. Bump and repin the Claude, Cursor, and portable revisions as well.

Useful? React with 👍 / 👎.

…and doctor activation states (#101)

Squash of the #452 branch (review rounds 1-7 folded in) ahead of the
rebase onto main, which now carries #482's AB7326.
…lugin install fails; exact standalone marketplace plans

- install: when marketplace add succeeded but plugin install / receipt write fails, remove the marketplace this run registered (its claim lived only in memory); a pre-existing marketplace is never touched
- install.mjs --uninstall --mode marketplace --plan names the receipt store, marketplaces root, and agent-bundle namespace it would prune, matching the completed run
… receipt-write failure, aggregate Claude lifecycle across scopes

- uninstall (core + install.mjs): a staged Cursor marketplace whose working tree differs from the receipted commit (or cannot be verified) is refused with AB7007 until --force; HEAD alone proved only the commit
- install: when the host install succeeded but the receipt write failed, the plugin registration is reversed before the marketplace this run created
- doctor AB7328: Claude multi-scope rows aggregate — a stage holds only for every listed copy; evidence names the disabled/unplaced/flagless scopes
- tests and docs for each
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/101-managed-lifecycle branch from 348cc00 to 2f9541b Compare September 4, 2026 02:34
…rtable (1.10.0) adapter revisions for the lifecycle capability rows and install-surface changes
… durable state during uninstall

Uninstalling a local Cursor copy (core uninstallBundle and the emitted
install.mjs --uninstall) now keeps a written PLUGIN_DATA directory behind a
remnant receipt that carries the cursorExpansion so a later
--purge-data --confirm-purge still finds it, prunes an empty installer-created
one together with its agent-bundle parents, and never touches a recorded path
outside this home's plugin-data. Doctor's AB7307 remnant diagnostic names the
PLUGIN_DATA directory. Restores the byte-identical host-install proofs for
cursor/portable and documents the behaviour in docs/diagnostics.md.
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

Head 96d31e07b: uninstall now treats the Cursor PLUGIN_DATA directory (~/.cursor/agent-bundle/plugin-data/<name>, created by the emitted installer's placeholder expansion) as receipt-owned durable state — kept behind a remnant receipt carrying cursorExpansion on --keep-data, purged on --purge-data --confirm-purge, pruned when empty, ignored when foreign. Restores the byte-identical cursor/portable host-install proofs; Doctor's AB7307 names the directory.

…made project/local installs elsewhere retain the marketplace and refuse a shared purge
…installed_plugins.json dependents in INSTALL.md, README, site (en/zh), and the changeset

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 96d31e07b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (recorded !== undefined) {
if (recorded !== expected) {
foreignPluginData = recorded;
} else if (await realDirectory(recorded, 'cursor') !== undefined) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject symlinked PLUGIN_DATA ancestors

When .cursor/agent-bundle or its plugin-data child is a symlink but the final <name> target is a real directory, this leaf-only lstat check succeeds. A confirmed purge later passes the path to recursive rm, which follows those ancestors and deletes the target directory outside the intended Cursor home; the existing ancestor validation covers only files below the plugin destination. Validate every ancestor between cursorRoot and the recorded PLUGIN_DATA directory in both the core and emitted uninstallers before reading or deleting it.

Useful? React with 👍 / 👎.

Comment on lines +563 to +564
...(data.emptyPluginData === undefined ? [] : [data.emptyPluginData]),
...(pluginDataRecorded ? [join(cursorRoot, 'agent-bundle', 'plugin-data'), join(cursorRoot, 'agent-bundle')] : []),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean an emptied PLUGIN_DATA remnant on rerun

When a keep-data uninstall leaves a remnant for a nonempty PLUGIN_DATA directory and that directory is subsequently emptied or removed, this candidate is never pruned: the remnantOnly && policy !== 'purge' shortcut below returns before simulation or execution. Repeated default uninstalls therefore leave the empty plugin root, receipt, and plugin-data parents indefinitely despite this change promising to prune empty PLUGIN_DATA; restrict the remnant no-op to cases where preserved data is still present, or process the empty candidate before returning.

Useful? React with 👍 / 👎.

Comment on lines +535 to +537
const preserved = [
...(stateOnly || entries.some(isPreservedRuntimeRoot) ? ['state/'] : []),
...(pluginData === undefined ? [] : [`the PLUGIN_DATA directory ${pluginData}`]),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify PLUGIN_DATA before reporting it as preserved

After uninstall --keep-data writes a remnant, deleting its external PLUGIN_DATA directory manually still makes AB7307 claim that the destination “holds only preserved runtime state” in that directory. A remnant moved to another home similarly reports the now-foreign recorded path as preserved even though uninstall deliberately ignores it. Check that the path is the expected current-home location and an existing real directory before including it in this diagnostic.

Useful? React with 👍 / 👎.

Comment thread website/docs/en/reference/cli.mdx Outdated
Comment on lines +126 to +127
| `--keep-data` | on | Keep durable runtime state (`state/`: state kernel, notices journal). This is the default; the flag makes it explicit. |
| `--purge-data` | off | Also remove durable runtime state. Refused (`AB7008`) without `--confirm-purge`. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document Cursor PLUGIN_DATA retention and purge

For an Agent Plugins pack with a stdio server, --keep-data now preserves ~/.cursor/agent-bundle/plugin-data/<name> and a confirmed purge recursively deletes it, but this public CLI reference still defines durable state only as state/; the installation guide, Chinese translation, package README, and changeset repeat that omission. Users can therefore authorize deletion of a second external data tree without the documented option semantics mentioning it. Update both localized matching pages and release-facing prose to describe PLUGIN_DATA ownership, retention, and purge behavior.

AGENTS.md reference: AGENTS.md:L71-L77

Useful? React with 👍 / 👎.

@ScriptedAlchemy
ScriptedAlchemy merged commit e647336 into main Sep 4, 2026
13 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the feat/101-managed-lifecycle branch September 4, 2026 03:35
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
… preserved data is gone, honest AB7307 (follow-up to #452) (#519)

* fix(uninstall): validate the agent-bundle and plugin-data ancestors before touching PLUGIN_DATA, consume a remnant whose preserved data is gone on a default rerun, and have Doctor name PLUGIN_DATA only when it is this home's real non-empty directory

* changeset: PLUGIN_DATA uninstall hardening follow-up

* changeset: attribute to #519

* fix(uninstall): treat an emptied state/ as absent (pruned with the exhausted remnant) and stop Doctor's AB7307 from inventing state/ for a remnant whose preserved data is gone
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add safe managed lifecycle for generated host bundles

1 participant