CB-567: compose charters per spawn
This commit is contained in:
@@ -796,14 +796,18 @@ class ClaudeCodeLauncherTest {
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static BridgedConfig.Fleet fleet(Map<String, String> charters, String tabLabel) {
|
||||
return new BridgedConfig.Fleet(Map.of(), Map.of(), Map.of(), Map.of(), charters, tabLabel);
|
||||
}
|
||||
|
||||
/** A profile with no {@code tabLabel:} of its own — the fleet template decides. */
|
||||
private ClaudeCodeLauncher labelService(FakeHerdr herdr, Supplier<String> fleetTemplate) {
|
||||
private ClaudeCodeLauncher labelService(FakeHerdr herdr, Supplier<BridgedConfig.Fleet> fleet) {
|
||||
BridgedConfig.Profile cfg = new BridgedConfig.Profile(
|
||||
"sonnet", "http://gx00.gw:8000", "sonnet", null, "BRIDGED_WORKER_TOKEN",
|
||||
List.of("claude"), "tab", "bridged-workers", null, null, null, null);
|
||||
return new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr),
|
||||
new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(),
|
||||
_ -> null, 0, 0L, fleetTemplate);
|
||||
_ -> null, 0, 0L, fleet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -814,7 +818,7 @@ class ClaudeCodeLauncherTest {
|
||||
@Test
|
||||
void theFleetTemplateNamesTheRoleTheMemberWasSpawnedFor() {
|
||||
FakeHerdr herdr = new FakeHerdr();
|
||||
ClaudeCodeLauncher svc = labelService(herdr, () -> "{role}: {profile} #{n}");
|
||||
ClaudeCodeLauncher svc = labelService(herdr, () -> fleet(null, "{role}: {profile} #{n}"));
|
||||
|
||||
svc.spawn(new SpawnRequest("sonnet", null, null, null, null, MemberRole.REVIEWER));
|
||||
|
||||
@@ -825,7 +829,7 @@ class ClaudeCodeLauncherTest {
|
||||
@Test
|
||||
void theCounterRunsPerRoleAndProfileNotPerFleet() {
|
||||
FakeHerdr herdr = new FakeHerdr();
|
||||
ClaudeCodeLauncher svc = labelService(herdr, () -> "{role}: {profile} #{n}");
|
||||
ClaudeCodeLauncher svc = labelService(herdr, () -> fleet(null, "{role}: {profile} #{n}"));
|
||||
|
||||
svc.spawn(new SpawnRequest("sonnet", null, null, null, null, MemberRole.DEV));
|
||||
svc.spawn(new SpawnRequest("sonnet", null, null, null, null, MemberRole.REVIEWER));
|
||||
@@ -839,7 +843,7 @@ class ClaudeCodeLauncherTest {
|
||||
@Test
|
||||
void aBlankFleetTemplateFallsBackToTheRoleFirstDefault() {
|
||||
FakeHerdr herdr = new FakeHerdr();
|
||||
labelService(herdr, () -> null).spawn(
|
||||
labelService(herdr, () -> fleet(null, null)).spawn(
|
||||
new SpawnRequest("sonnet", null, null, null, null, MemberRole.ARCHITECT));
|
||||
|
||||
assertEquals(List.of("architect: sonnet #1"), tabLabels(herdr));
|
||||
@@ -855,7 +859,7 @@ class ClaudeCodeLauncherTest {
|
||||
List.of("claude"), "tab", "bridged-workers", "pinned {profile}", null, null, null);
|
||||
new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr),
|
||||
new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(),
|
||||
_ -> null, 0, 0L, () -> "{role}: {profile} #{n}")
|
||||
_ -> null, 0, 0L, () -> fleet(null, "{role}: {profile} #{n}"))
|
||||
.spawn(new SpawnRequest("sonnet", null, null, null, null, MemberRole.REVIEWER));
|
||||
|
||||
assertEquals(List.of("pinned sonnet"), tabLabels(herdr));
|
||||
@@ -870,7 +874,7 @@ class ClaudeCodeLauncherTest {
|
||||
void theTemplateIsReadOnEverySpawnSoAnEditTakesEffect() {
|
||||
FakeHerdr herdr = new FakeHerdr();
|
||||
AtomicReference<String> template = new AtomicReference<>("{role}: {profile} #{n}");
|
||||
ClaudeCodeLauncher svc = labelService(herdr, template::get);
|
||||
ClaudeCodeLauncher svc = labelService(herdr, () -> fleet(null, template.get()));
|
||||
|
||||
svc.spawn(new SpawnRequest("sonnet", null, null, null, null, MemberRole.DEV));
|
||||
template.set("[{profile}] {role} {n}");
|
||||
|
||||
@@ -77,7 +77,7 @@ class CompositePeerLauncherTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Launch buildLaunch(BridgedConfig.Profile cfg) {
|
||||
protected Launch buildLaunch(BridgedConfig.Profile cfg, LaunchSpec spec) {
|
||||
return new Launch(Map.of(), List.of());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package dev.ltms.bridged.member;
|
||||
|
||||
import dev.ltms.bridged.config.BridgedConfig;
|
||||
import dev.ltms.bridged.herdr.AgentControl;
|
||||
import dev.ltms.bridged.herdr.FakeHerdr;
|
||||
import dev.ltms.bridged.herdr.WorkspaceControl;
|
||||
import dev.ltms.bridged.peer.Capability;
|
||||
import dev.ltms.bridged.peer.MemberRole;
|
||||
import dev.ltms.bridged.peer.SpawnRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class HerdrPeerLauncherCharterTest {
|
||||
|
||||
@Test
|
||||
void readsAndComposesTheFleetCharterForEachSpawn() {
|
||||
AtomicReference<BridgedConfig.Fleet> fleet = new AtomicReference<>(fleet(Map.of()));
|
||||
CapturingLauncher launcher = new CapturingLauncher(fleet::get);
|
||||
|
||||
launcher.spawn(new SpawnRequest("mcp", null, null, null, null, MemberRole.DEV));
|
||||
fleet.set(fleet(Map.of("dev", "role charter")));
|
||||
launcher.spawn(new SpawnRequest("mcp", null, null, null, null, MemberRole.DEV));
|
||||
launcher.spawn(new SpawnRequest("no-mcp", null, null, null, null, MemberRole.DEV));
|
||||
|
||||
assertEquals(HerdrPeerLauncher.REPLY_CHARTER, launcher.specs.get(0).charter(),
|
||||
"without a role charter, MCP profiles receive only the reply charter");
|
||||
assertEquals("role charter\n\n" + HerdrPeerLauncher.REPLY_CHARTER, launcher.specs.get(1).charter(),
|
||||
"the changed supplier value is read for the next spawn and the reply rule is last");
|
||||
assertEquals("role charter", launcher.specs.get(2).charter(),
|
||||
"a role charter does not depend on an MCP mount");
|
||||
}
|
||||
|
||||
private static BridgedConfig.Fleet fleet(Map<String, String> charters) {
|
||||
return new BridgedConfig.Fleet(Map.of(), Map.of(), Map.of(), Map.of(), charters, null);
|
||||
}
|
||||
|
||||
private static final class CapturingLauncher extends HerdrPeerLauncher {
|
||||
private final List<LaunchSpec> specs = new ArrayList<>();
|
||||
|
||||
CapturingLauncher(Supplier<BridgedConfig.Fleet> fleet) {
|
||||
super("test", new AgentControl(new FakeHerdr()), new WorkspaceControl(new FakeHerdr()),
|
||||
Map.of("mcp", profile("mcp", "http://bridge"),
|
||||
"no-mcp", profile("no-mcp", null)),
|
||||
"mcp", _ -> null, 0, () -> 0L, () -> { }, fleet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Launch buildLaunch(BridgedConfig.Profile cfg, LaunchSpec spec) {
|
||||
specs.add(spec);
|
||||
return new Launch(Map.of(), List.of("test"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Capability> capabilities() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
private static BridgedConfig.Profile profile(String name, String mcpUrl) {
|
||||
return new BridgedConfig.Profile(name, "http://gx00.gw:8000", null, null,
|
||||
"BRIDGED_WORKER_TOKEN", List.of("test"), "pane", null, null, mcpUrl, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user