Skip to content

Commit cbc13d9

Browse files
fix(install): rebase onto main — renumber uninstall/lifecycle codes around #479 (AB7006→AB7009, AB7325→AB7328); write the Claude receipt before the load verdict
1 parent 428a28d commit cbc13d9

2 files changed

Lines changed: 8 additions & 5 deletions

File tree

‎packages/agent-bundle/src/install/install.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,9 @@ export const parsePublicHostInventory = (
372372
}
373373
if (options.scope !== undefined && row['scope'] !== options.scope) continue;
374374
const errors = claudePluginRowErrors(row);
375-
entries.push({
376-
...(errors.length === 0 ? {} : { errors }),
377375
entries.push({
378376
...(typeof row['enabled'] === 'boolean' ? { enabled: row['enabled'] } : {}),
377+
...(errors.length === 0 ? {} : { errors }),
379378
installPath: row['installPath'],
380379
scope: row['scope'],
381380
version: row['version'],

‎packages/agent-bundle/tests/install.test.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,26 +385,30 @@ it('fails a Claude install (AB7006) when plugin list --json reports load errors
385385
listings += 1;
386386
return listings === 1 ? '[]' : claudeRefusedInventory(installed);
387387
});
388-
const error = await installBundle({ commandRunner: fresh.runner, from: fixture.from, host: 'claude', scope: 'user' })
388+
const error = await installBundle({ ...isolated(fixture), commandRunner: fresh.runner, from: fixture.from, host: 'claude', scope: 'user' })
389389
.catch((failure: unknown) => failure);
390390
expect(error).toBeInstanceOf(DiagnosticError);
391391
const [diagnostic] = (error as DiagnosticError).diagnostics;
392392
expect(diagnostic).toMatchObject({ code: 'AB7006', severity: 'error', target: 'claude' });
393393
expect(diagnostic?.message).toContain(`claude refused to load install-fixture@install-fixture-marketplace (version 1.2.3) at ${JSON.stringify(installed)} (scope user) after installation`);
394394
expect(diagnostic?.message).toContain('Duplicate hooks file detected');
395395
expect(diagnostic?.message).toContain('--replace');
396+
// The receipt lands before the load verdict, so the refused copy stays receipt-owned for `uninstall`.
396397
expect(fresh.calls.map((call) => call.args.join(' '))).toEqual([
397398
'plugin list --json',
399+
'plugin marketplace list --json',
398400
`plugin marketplace add ${fixture.bundleRoot}`,
399401
'plugin install install-fixture@install-fixture-marketplace --scope user',
400402
'plugin list --json',
401403
]);
404+
expect(await readInstallReceiptFile(join(fixture.cleanupRoot, 'claude-config', 'agent-bundle', 'receipts', 'install-fixture.user.json')))
405+
.toMatchObject({ plugin: 'install-fixture', scope: 'user' });
402406

403407
// A byte-identical copy the host already refuses is never "already installed": reinstalling the same
404408
// bytes cannot help, so the defect is reported instead of a success.
405409
await cp(fixture.bundleRoot, installed, { recursive: true });
406410
const identical = recordingRunner((call) => isInventoryCall(call) ? claudeRefusedInventory(installed) : '');
407-
const existing = await installBundle({ commandRunner: identical.runner, from: fixture.from, host: 'claude', scope: 'user' })
411+
const existing = await installBundle({ ...isolated(fixture), commandRunner: identical.runner, from: fixture.from, host: 'claude', scope: 'user' })
408412
.catch((failure: unknown) => failure);
409413
expect(existing).toBeInstanceOf(DiagnosticError);
410414
expect((existing as DiagnosticError).diagnostics[0]).toMatchObject({ code: 'AB7006', target: 'claude' });
@@ -413,7 +417,7 @@ it('fails a Claude install (AB7006) when plugin list --json reports load errors
413417

414418
// Healthy rows (no `errors` key) keep the install result unchanged.
415419
const healthy = recordingRunner((call) => isInventoryCall(call) ? claudeInventory(installed) : '');
416-
await expect(installBundle({ commandRunner: healthy.runner, from: fixture.from, host: 'claude', scope: 'user' }))
420+
await expect(installBundle({ ...isolated(fixture), commandRunner: healthy.runner, from: fixture.from, host: 'claude', scope: 'user' }))
417421
.resolves.toMatchObject({ state: 'already-installed' });
418422
} finally {
419423
await rm(fixture.cleanupRoot, { force: true, recursive: true });

0 commit comments

Comments
 (0)