master planner: topology info panel under the gallery
Selecting a card in the strip expands into an info panel below with: - Coordinator badge (green when the topology has a lead, gray when leaderless — mirrors what cm-orchestrator's planner reads off the graph) - Communication pattern one-liner - 'When to use' guidance - Auto-staffed role distribution chips (from the catalog's role_distribution — same numbers TeamWizard apportions) - ASCII sketch of the shape Cheat sheet is client-side (TOPO_DETAIL) so the expansion is instant on selection — mirrors the exec-plan semantics baked into cm-orchestrator.
This commit is contained in:
@@ -91,6 +91,87 @@ type CatalogEntry = {
|
|||||||
role_distribution: { role: string; weight: number }[];
|
role_distribution: { role: string; weight: number }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Per-topology comm/coordinator/usage cheat sheet. Kept in the client so the
|
||||||
|
// info panel can render immediately after a selection without a second fetch;
|
||||||
|
// mirrors the semantics baked into cm-orchestrator's plan_steps().
|
||||||
|
const TOPO_DETAIL: Record<
|
||||||
|
string,
|
||||||
|
{ coordinator: string | null; comm: string; whenToUse: string; diagram: string }
|
||||||
|
> = {
|
||||||
|
hierarchical: {
|
||||||
|
coordinator: "root",
|
||||||
|
comm: "Root plans → children work in parallel with the plan as context → root synthesizes their outputs.",
|
||||||
|
whenToUse: "Decompose a goal, fan out, then produce one coherent answer.",
|
||||||
|
diagram: " root\n / | \\\n c1 c2 c3\n \\ | /\n root(synth)",
|
||||||
|
},
|
||||||
|
hub_spoke: {
|
||||||
|
coordinator: "hub",
|
||||||
|
comm: "Hub delegates a shard of the task to each spoke, then aggregates their replies.",
|
||||||
|
whenToUse: "One coordinator briefing specialists and collecting results.",
|
||||||
|
diagram: " hub\n / | | \\\n s1 s2 s3 s4\n \\ | | /\n hub",
|
||||||
|
},
|
||||||
|
star_moe: {
|
||||||
|
coordinator: "router",
|
||||||
|
comm: "Router classifies each subtask and dispatches it to the expert whose specialty fits.",
|
||||||
|
whenToUse: "Mixed-domain tasks where routing beats broadcasting.",
|
||||||
|
diagram: " router\n /|\\ \\\n e1 e2 e3 e4",
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
coordinator: "auctioneer",
|
||||||
|
comm: "Auctioneer posts tasks; agents bid on fit/cost; the winner works.",
|
||||||
|
whenToUse: "When you want cost/quality trade-offs surfaced automatically.",
|
||||||
|
diagram: " auctioneer\n | task\n a1 a2 a3\n (bids)",
|
||||||
|
},
|
||||||
|
pipeline: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Linear stages: stage N's output is stage N+1's only input.",
|
||||||
|
whenToUse: "Multi-step transforms — draft → critique → polish.",
|
||||||
|
diagram: "s1 → s2 → s3 → s4",
|
||||||
|
},
|
||||||
|
ring: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Cyclic — each pass hands the current draft to the next node, refining each lap.",
|
||||||
|
whenToUse: "Iterative polish where each agent adds a different pass.",
|
||||||
|
diagram: "a1 → a2\n ↑ ↓\na4 ← a3",
|
||||||
|
},
|
||||||
|
swarm: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Many parallel workers attack the goal; a synth step aggregates their outputs into consensus.",
|
||||||
|
whenToUse: "Breadth-first exploration; parallelism > coordination.",
|
||||||
|
diagram: "w1 w2 w3 … wN\n \\ | | /\n synth",
|
||||||
|
},
|
||||||
|
flat: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Independent peers work in parallel; nobody routes, nobody coordinates.",
|
||||||
|
whenToUse: "Trivially parallel tasks with no cross-dependencies.",
|
||||||
|
diagram: "a1 a2 a3 a4",
|
||||||
|
},
|
||||||
|
holacratic: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Roles self-organize into circles; whichever role holds the ball works.",
|
||||||
|
whenToUse: "Dynamic responsibilities where the lead can shift per subtask.",
|
||||||
|
diagram: "( circle 1 )\n( circle 2 )\n( shared roles )",
|
||||||
|
},
|
||||||
|
mesh: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Dense peer-to-peer exchange until the network converges on an answer.",
|
||||||
|
whenToUse: "Consensus problems — every agent's view weighs on every other.",
|
||||||
|
diagram: "a1—a2\n | X |\na3—a4",
|
||||||
|
},
|
||||||
|
blackboard: {
|
||||||
|
coordinator: null,
|
||||||
|
comm: "Shared workspace agents read/write freely; no fixed protocol between them.",
|
||||||
|
whenToUse: "Long-lived shared state, opportunistic contributors.",
|
||||||
|
diagram: "a1 a2 a3\n \\ | /\n [ blackboard ]",
|
||||||
|
},
|
||||||
|
debate: {
|
||||||
|
coordinator: "judge",
|
||||||
|
comm: "Proposer and critic argue in rounds; a judge decides at the end.",
|
||||||
|
whenToUse: "Adversarial verification — force each claim to survive a challenger.",
|
||||||
|
diagram: "proposer ⇄ critic\n ↓\n judge",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export function MasterPlannerModal({ onClose }: { onClose: () => void }) {
|
export function MasterPlannerModal({ onClose }: { onClose: () => void }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [mode, setMode] = useState<Mode>("specialists");
|
const [mode, setMode] = useState<Mode>("specialists");
|
||||||
@@ -481,6 +562,100 @@ function TopologyStrip({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{selected ? (
|
||||||
|
<TopologyInfoPanel
|
||||||
|
entry={catalog.find((c) => c.kind === selected)}
|
||||||
|
detail={TOPO_DETAIL[selected]}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TopologyInfoPanel({
|
||||||
|
entry,
|
||||||
|
detail,
|
||||||
|
}: {
|
||||||
|
entry: CatalogEntry | undefined;
|
||||||
|
detail: { coordinator: string | null; comm: string; whenToUse: string; diagram: string } | undefined;
|
||||||
|
}) {
|
||||||
|
if (!entry) return null;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 8,
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "minmax(0, 1fr) 168px",
|
||||||
|
gap: 12,
|
||||||
|
padding: "10px 12px",
|
||||||
|
borderRadius: 10,
|
||||||
|
border: "1px solid rgba(201,138,240,.22)",
|
||||||
|
background: "rgba(201,138,240,.05)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", gap: 6, minWidth: 0 }}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 700, color: "#f3f3f5" }}>{entry.name}</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 9.5,
|
||||||
|
padding: "2px 7px",
|
||||||
|
borderRadius: 5,
|
||||||
|
background: detail?.coordinator ? "rgba(127,208,160,.14)" : "rgba(255,255,255,.06)",
|
||||||
|
color: detail?.coordinator ? "#7fd0a0" : "#8a8a92",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{detail?.coordinator ? `coordinator: ${detail.coordinator}` : "leaderless"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{detail ? (
|
||||||
|
<div style={{ fontSize: 11.5, color: "#cfcfd5", lineHeight: 1.5 }}>{detail.comm}</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ fontSize: 11.5, color: "#cfcfd5", lineHeight: 1.5 }}>{entry.description}</div>
|
||||||
|
)}
|
||||||
|
{detail ? (
|
||||||
|
<div style={{ fontFamily: mono, fontSize: 10.5, color: "#c98af0" }}>
|
||||||
|
when to use — <span style={{ color: "#eaeaee" }}>{detail.whenToUse}</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{entry.role_distribution.length ? (
|
||||||
|
<div style={{ display: "flex", flexWrap: "wrap", gap: 4, marginTop: 2 }}>
|
||||||
|
{entry.role_distribution.map((r) => (
|
||||||
|
<span
|
||||||
|
key={r.role}
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 10,
|
||||||
|
padding: "2px 6px",
|
||||||
|
borderRadius: 5,
|
||||||
|
background: "rgba(255,255,255,.05)",
|
||||||
|
color: "#cfcfd5",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{r.role} · {Math.round(r.weight * 100)}%
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
margin: 0,
|
||||||
|
padding: 8,
|
||||||
|
borderRadius: 6,
|
||||||
|
background: "#0a0a0d",
|
||||||
|
border: "1px solid rgba(255,255,255,.06)",
|
||||||
|
color: "#c98af0",
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 10.5,
|
||||||
|
lineHeight: 1.35,
|
||||||
|
whiteSpace: "pre",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{detail?.diagram ?? "—"}
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user