* fix(upgrade): refresh installer-written agent surfaces after a binary upgrade codegraph upgrade swapped the binary but never revisited what earlier installs wrote into CLAUDE.md / AGENTS.md / GEMINI.md and the agent configs, so sections written by a pre-1.0 installer kept teaching agents a multi-tool surface (including tools that no longer exist) months of releases later. The install path already self-heals everything it owns, but nothing ever called it on upgrade. - codegraph install --refresh: non-interactive sweep that re-runs install() for already-configured targets only — never a first install; permissions and prompt-hook choices are preserved. - codegraph upgrade spawns it via the freshly-installed binary after a successful swap (the still-running old process would only rewrite its own stale template). Gated on PATH resolution and the CODEGRAPH_NO_INSTALL_REFRESH=1 kill-switch; never fatal to the upgrade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(installer): clarify refresh change reporting --------- Co-authored-by: xuing <np2v9bvbbs@privaterelay.appleid.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Colby McHenry <me@colbymchenry.com>
This commit is contained in:
co-authored by
Claude Fable 5
xuing
Colby McHenry
parent
63eb488ed4
commit
386bff0f84
@@ -408,6 +408,112 @@ describe('runUpgrade', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Post-upgrade self-heal of installed agent surfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('post-upgrade refresh of installed agent surfaces', () => {
|
||||
it('runs `codegraph install --refresh` via the NEW binary after a successful npm upgrade', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
});
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
// The refresh is spawned AFTER the binary swap, so the fresh install
|
||||
// (with the current templates) does the writing — not this process.
|
||||
const last = calls.runs[calls.runs.length - 1];
|
||||
expect(last?.cmd).toBe('codegraph');
|
||||
expect(last?.args).toEqual(['install', '--refresh']);
|
||||
});
|
||||
|
||||
it('runs the Windows .cmd launcher through cmd.exe', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
platform: 'win32',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
});
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
const last = calls.runs[calls.runs.length - 1];
|
||||
expect(last?.cmd).toBe('cmd.exe');
|
||||
expect(last?.args).toEqual(['/d', '/s', '/c', 'codegraph install --refresh']);
|
||||
});
|
||||
|
||||
it('skips the refresh when `codegraph` is not resolvable on PATH', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
// default hasCommand resolves only curl
|
||||
});
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
expect(calls.runs.filter((r) => r.cmd === 'codegraph')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('a failing refresh warns but does not fail the upgrade', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
});
|
||||
deps.run = (cmd, args, env) => {
|
||||
calls.runs.push({ cmd, args, env });
|
||||
return cmd === 'codegraph' ? 1 : 0;
|
||||
};
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
expect(calls.logs.join('\n')).toMatch(/install --refresh/);
|
||||
});
|
||||
|
||||
it('does not run after a failed upgrade', async () => {
|
||||
const { deps, calls } = makeDeps(
|
||||
{
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
},
|
||||
1
|
||||
);
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(1);
|
||||
expect(calls.runs.filter((r) => r.cmd === 'codegraph')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('respects the CODEGRAPH_NO_INSTALL_REFRESH kill-switch', async () => {
|
||||
process.env.CODEGRAPH_NO_INSTALL_REFRESH = '1';
|
||||
try {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
});
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
expect(calls.runs.filter((r) => r.cmd === 'codegraph')).toHaveLength(0);
|
||||
} finally {
|
||||
delete process.env.CODEGRAPH_NO_INSTALL_REFRESH;
|
||||
}
|
||||
});
|
||||
|
||||
it('skips the refresh when the version probe says a stale install shadows the new one', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
method: { kind: 'npm', scope: 'global' },
|
||||
currentVersion: '0.9.8',
|
||||
hasCommand: (cmd) => cmd === 'codegraph',
|
||||
capture: () => ({ code: 0, stdout: '0.9.8\n' }), // PATH still serves the OLD version
|
||||
});
|
||||
const code = await runUpgrade({}, deps);
|
||||
expect(code).toBe(0);
|
||||
// Spawning `codegraph install --refresh` would execute the shadowed stale
|
||||
// binary — the exact staleness the refresh exists to heal.
|
||||
expect(calls.runs.filter((r) => r.cmd === 'codegraph')).toHaveLength(0);
|
||||
expect(calls.logs.join('\n')).toMatch(/run `codegraph install --refresh` once the PATH is fixed/);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Post-upgrade version probe — does the PATH-resolved `codegraph` serve the
|
||||
// version we just installed, in THIS terminal?
|
||||
|
||||
Reference in New Issue
Block a user