feat(fleet): reconcile local roster state (#785)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

This commit was merged in pull request #785.
This commit is contained in:
2026-07-15 15:03:31 +00:00
parent c593a15ef8
commit 499090508e
12 changed files with 1888 additions and 45 deletions

View File

@@ -81,9 +81,11 @@ describe('registerFleetCommand', () => {
expect(fleet).toBeDefined();
expect(fleet!.commands.map((command) => command.name()).sort()).toEqual([
'add',
'apply',
'backlog',
'create',
'delete',
'doctor',
'get',
'init',
'install',
@@ -93,6 +95,7 @@ describe('registerFleetCommand', () => {
'profile',
'provision',
'ps',
'reconcile',
'remove',
'restart',
'start',
@@ -490,7 +493,13 @@ describe('fleet command construction', () => {
]);
});
it('runs fleet status through injected runner without touching tmux in tests', async () => {
it('runs legacy fleet status through injected runner without touching tmux in tests', async () => {
const home = await tempDir();
await mkdir(join(home, 'fleet'), { recursive: true });
await writeFile(
join(home, 'fleet', 'roster.yaml'),
'version: 1\ntransport: tmux\nagents: []\n',
);
const calls: string[][] = [];
const runner: CommandRunner = async (command, args) => {
calls.push([command, ...args]);
@@ -498,11 +507,14 @@ describe('fleet command construction', () => {
};
const program = new Command();
program.exitOverride();
registerFleetCommand(program, { runner });
registerFleetCommand(program, { runner, mosaicHome: home });
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
expect(calls).toEqual([['systemctl', '--user', 'status', 'mosaic-tmux-holder.service']]);
try {
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
expect(calls).toEqual([['systemctl', '--user', 'status', 'mosaic-tmux-holder.service']]);
} finally {
await rm(home, { recursive: true, force: true });
}
});
it('verifies liveness with tmux has-session and does not trust systemd active exited', async () => {
@@ -618,13 +630,19 @@ describe('fleet command construction', () => {
).rejects.toThrow('Unsupported fleet profile');
});
it('sets process exitCode when status runner fails', async () => {
it('sets process exitCode when legacy status runner fails', async () => {
const home = await tempDir();
await mkdir(join(home, 'fleet'), { recursive: true });
await writeFile(
join(home, 'fleet', 'roster.yaml'),
'version: 1\ntransport: tmux\nagents: []\n',
);
const originalExitCode = process.exitCode;
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
const runner: CommandRunner = async () => ({ stdout: '', stderr: 'missing\n', exitCode: 3 });
const program = new Command();
program.exitOverride();
registerFleetCommand(program, { runner });
registerFleetCommand(program, { runner, mosaicHome: home });
try {
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
@@ -632,6 +650,7 @@ describe('fleet command construction', () => {
} finally {
process.exitCode = originalExitCode;
stderrSpy.mockRestore();
await rm(home, { recursive: true, force: true });
}
});