feat(fleet): enhancer role + two-agent floor (orchestrator + enhancer) (#615)
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #615.
This commit is contained in:
@@ -881,11 +881,13 @@ export function registerFleetCommand(program: Command, deps: FleetCommandDeps =
|
||||
await mkdir(dirname(destination), { recursive: true });
|
||||
await writeFile(destination, content);
|
||||
|
||||
// Guarantee R5: exactly one orchestrator for every profile except the
|
||||
// sanctioned no-orchestrator `minimal` preset. A mismatch means a
|
||||
// corrupted/edited preset — fail hard rather than write a malformed fleet.
|
||||
// Guarantee the two-agent floor: exactly one orchestrator AND at least
|
||||
// one enhancer for every profile except the sanctioned no-orchestrator
|
||||
// `minimal` preset. A mismatch means a corrupted/edited preset — fail hard
|
||||
// rather than write a malformed fleet.
|
||||
const written = await loadFleetRoster(destination);
|
||||
const orchCount = countOrchestrators(written);
|
||||
const enhancerCount = countEnhancers(written);
|
||||
if (profile === 'minimal') {
|
||||
console.log(
|
||||
`Initialized ${profile} fleet: ${written.agents.length} agent(s) (no orchestrator). Next: mosaic fleet install`,
|
||||
@@ -895,10 +897,17 @@ export function registerFleetCommand(program: Command, deps: FleetCommandDeps =
|
||||
`Fleet init failed: the "${profile}" roster has ${orchCount} orchestrator agent(s), ` +
|
||||
`expected exactly 1 (R5). The preset may be corrupted — re-install the framework.`,
|
||||
);
|
||||
} else if (enhancerCount < 1) {
|
||||
throw new Error(
|
||||
`Fleet init failed: the "${profile}" roster has no enhancer agent. Every fleet keeps an ` +
|
||||
`orchestrator + enhancer minimum (two-agent floor). The preset may be corrupted — ` +
|
||||
`re-install the framework.`,
|
||||
);
|
||||
} else {
|
||||
const workerCount = written.agents.length - 1;
|
||||
const workerCount = written.agents.length - 1 - enhancerCount;
|
||||
console.log(
|
||||
`Initialized ${profile} fleet: 1 orchestrator + ${workerCount} agent(s). Next: mosaic fleet install`,
|
||||
`Initialized ${profile} fleet: 1 orchestrator + ${enhancerCount} enhancer(s) + ` +
|
||||
`${workerCount} worker(s). Next: mosaic fleet install`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1945,6 +1954,15 @@ export function countOrchestrators(roster: FleetRoster): number {
|
||||
return roster.agents.filter((a) => a.className === 'orchestrator').length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count enhancer agents in a parsed roster. The two-agent floor (north-star)
|
||||
* requires every non-minimal fleet to carry at least one enhancer alongside the
|
||||
* sole orchestrator.
|
||||
*/
|
||||
export function countEnhancers(roster: FleetRoster): number {
|
||||
return roster.agents.filter((a) => a.className === 'enhancer').length;
|
||||
}
|
||||
|
||||
/** Valid runtime identifiers for fleet agents. */
|
||||
export const VALID_FLEET_RUNTIMES: readonly string[] = [
|
||||
'pi',
|
||||
@@ -1987,6 +2005,15 @@ export function removeAgentFromRoster(roster: FleetRoster, name: string): FleetR
|
||||
`Cannot remove agent "${name}": it is the sole orchestrator. Add another orchestrator first (R5).`,
|
||||
);
|
||||
}
|
||||
// Two-agent floor: never drop the last enhancer (the continuous-improvement
|
||||
// loop). Symmetric with the sole-orchestrator guard.
|
||||
const remainingEnhancerCount = remaining.filter((a) => a.className === 'enhancer').length;
|
||||
if (remainingEnhancerCount === 0 && agent.className === 'enhancer') {
|
||||
throw new Error(
|
||||
`Cannot remove agent "${name}": it is the sole enhancer. Every fleet keeps at least one ` +
|
||||
`enhancer (two-agent floor). Add another enhancer first.`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
...roster,
|
||||
agents: remaining,
|
||||
|
||||
Reference in New Issue
Block a user