diff --git a/app/src/components/marketplace/InstalledPane.test.tsx b/app/src/components/marketplace/InstalledPane.test.tsx index 0ed2572..d691462 100644 --- a/app/src/components/marketplace/InstalledPane.test.tsx +++ b/app/src/components/marketplace/InstalledPane.test.tsx @@ -83,6 +83,31 @@ describe("InstalledPane", () => { ); }); + it("PR review #8: an update belongs only to the install pinned at its commit", () => { + const C = "c".repeat(40); + // The same agent is installed globally at A and in p1 at C. Only the A + // install has an update (A → B); C is unchanged at head. + useAppState.setState({ + projects: [ + { + id: "p1", + name: "api", + status: "running", + marketplace_installs: [{ marketplace_id: "m1", kind: "agent", key: "rev", commit: C }], + marketplace_disabled: [], + }, + ] as unknown as Project[], + }); + const mp = api({ + updates: [{ item: { marketplace_id: "m1", kind: "agent", key: "rev" }, pinned: A, head: B }], + }); + render(); + const global = screen.getByTestId("installed-global"); + expect(within(global).getByRole("button", { name: "Review update for rev" })).toBeInTheDocument(); + const proj = screen.getByTestId("installed-project-p1"); + expect(within(proj).queryByRole("button", { name: "Review update for rev" })).not.toBeInTheDocument(); + }); + it("I2: accepts the head that was reviewed even if the update list moves on", async () => { const C = "c".repeat(40); const item = { marketplace_id: "m1", kind: "agent" as const, key: "rev" }; diff --git a/app/src/components/marketplace/InstalledPane.tsx b/app/src/components/marketplace/InstalledPane.tsx index 53f507a..5dd52fc 100644 --- a/app/src/components/marketplace/InstalledPane.tsx +++ b/app/src/components/marketplace/InstalledPane.tsx @@ -32,13 +32,15 @@ export default function InstalledPane({ mp }: { mp: MarketplaceApi }) { const nameOf = (id: string) => marketplaces.find((m) => m.id === id)?.name ?? id; const globalInstalls = appSettings?.global_marketplace_installs ?? []; + /** The update for this very install: same item *and* pinned at the same + * commit (PR review #8) — updates are listed per (item, pinned commit). */ const updateFor = (i: MarketplaceInstall) => mp.updates.find( (u) => u.item.marketplace_id === i.marketplace_id && u.item.kind === i.kind && u.item.key === i.key && - u.head !== i.commit, + u.pinned === i.commit, ); /** Hooks only (spec §3, preflight F8): the rendered commands at head, so the