feat(fleet): harden v1 migration preview
Some checks failed
ci/woodpecker/pr/ci Pipeline failed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jarvis
2026-07-16 00:57:33 -05:00
parent 0e814324ca
commit 04891422b3
14 changed files with 477 additions and 82 deletions

View File

@@ -198,6 +198,26 @@ describe('fleet roster parsing', () => {
expect(getRosterAgent(roster, 'canary-pi').runtime).toBe('pi');
});
it('uses /clear for an explicitly declared empty pi runtime config', async () => {
cleanup = await tempDir();
const rosterPath = join(cleanup, 'roster.yaml');
await writeFile(
rosterPath,
[
'version: 1',
'transport: tmux',
'runtimes:',
' pi: {}',
'agents:',
' - name: canary-pi',
' runtime: pi',
].join('\n'),
);
const loaded = await loadFleetRoster(rosterPath);
expect(loaded.runtimes.pi?.resetCommand).toBe('/clear');
});
it('accepts optional agent alias and provider metadata without requiring them', async () => {
cleanup = await tempDir();
const rosterPath = join(cleanup, 'roster.yaml');
@@ -271,6 +291,32 @@ describe('fleet roster parsing', () => {
expect(env).toContain('MOSAIC_TMUX_SOCKET=\n');
});
it('preserves home-relative traversal until the shared environment boundary rejects it', async () => {
for (const workingDirectory of ['~/../escape', '~/src/../../escape']) {
cleanup = await tempDir();
const rosterPath = join(cleanup, 'roster.json');
await writeFile(
rosterPath,
JSON.stringify({
version: 1,
transport: 'tmux',
defaults: { working_directory: workingDirectory },
agents: [{ name: 'coder0', runtime: 'pi' }],
}),
);
const roster = await loadFleetRoster(rosterPath);
expect(() => generateAgentEnv(roster, getRosterAgent(roster, 'coder0'))).toThrow(
expect.objectContaining({
diagnostic: expect.objectContaining({
code: 'unsafe-path',
key: 'MOSAIC_AGENT_WORKDIR',
}),
}),
);
}
});
it('generates deterministic per-agent EnvironmentFile content', async () => {
cleanup = await tempDir();
const rosterPath = join(cleanup, 'roster.json');