mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-20 12:13:38 +08:00
Consolidate agent harness course into 19 lessons
This commit is contained in:
@@ -25,8 +25,6 @@ import s16Annotations from "@/data/annotations/s16.json";
|
||||
import s17Annotations from "@/data/annotations/s17.json";
|
||||
import s18Annotations from "@/data/annotations/s18.json";
|
||||
import s19Annotations from "@/data/annotations/s19.json";
|
||||
import s20Annotations from "@/data/annotations/s20.json";
|
||||
import s21Annotations from "@/data/annotations/s21.json";
|
||||
|
||||
interface Decision {
|
||||
id: string;
|
||||
@@ -62,8 +60,6 @@ const ANNOTATIONS: Record<string, AnnotationFile> = {
|
||||
s17: s17Annotations as AnnotationFile,
|
||||
s18: s18Annotations as AnnotationFile,
|
||||
s19: s19Annotations as AnnotationFile,
|
||||
s20: s20Annotations as AnnotationFile,
|
||||
s21: s21Annotations as AnnotationFile,
|
||||
};
|
||||
|
||||
interface DesignDecisionsProps {
|
||||
|
||||
@@ -28,8 +28,6 @@ const scenarioModules: Record<string, () => Promise<{ default: Scenario }>> = {
|
||||
s17: () => import("@/data/scenarios/s17.json") as Promise<{ default: Scenario }>,
|
||||
s18: () => import("@/data/scenarios/s18.json") as Promise<{ default: Scenario }>,
|
||||
s19: () => import("@/data/scenarios/s19.json") as Promise<{ default: Scenario }>,
|
||||
s20: () => import("@/data/scenarios/s20.json") as Promise<{ default: Scenario }>,
|
||||
s21: () => import("@/data/scenarios/s21.json") as Promise<{ default: Scenario }>,
|
||||
};
|
||||
|
||||
interface AgentLoopSimulatorProps {
|
||||
|
||||
@@ -21,11 +21,9 @@ const visualizations: Record<
|
||||
s12: lazy(() => import("./s07-task-system")),
|
||||
s13: lazy(() => import("./s08-background-tasks")),
|
||||
s14: lazy(() => import("./s14-cron-scheduler")),
|
||||
s15: lazy(() => import("./s10-team-protocols")),
|
||||
s16: lazy(() => import("./s11-autonomous-agents")),
|
||||
s17: lazy(() => import("./s12-worktree-task-isolation")),
|
||||
s18: lazy(() => import("./s19-mcp-tools")),
|
||||
s19: lazy(() => import("./s20-comprehensive")),
|
||||
s15: lazy(() => import("./s15-team-runtime")),
|
||||
s16: lazy(() => import("./s16-mcp-tools")),
|
||||
s17: lazy(() => import("./s17-integrated-harness")),
|
||||
};
|
||||
|
||||
export function SessionVisualization({ version }: { version: string }) {
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { Inbox, MessageSquareText, UsersRound } from "lucide-react";
|
||||
import { StepControls } from "@/components/visualizations/shared/step-controls";
|
||||
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type AgentId = "lead" | "coder" | "reviewer";
|
||||
|
||||
interface Mail {
|
||||
id: string;
|
||||
from: AgentId;
|
||||
to: AgentId;
|
||||
subject: string;
|
||||
body: string;
|
||||
appearsAt: number;
|
||||
consumedAt?: number;
|
||||
}
|
||||
|
||||
const AGENTS: { id: AgentId; label: string; role: string }[] = [
|
||||
{ id: "lead", label: "Lead", role: "splits work and reads results" },
|
||||
{ id: "coder", label: "Coder", role: "implements one slice" },
|
||||
{ id: "reviewer", label: "Reviewer", role: "checks the result" },
|
||||
];
|
||||
|
||||
const MAIL: Mail[] = [
|
||||
{
|
||||
id: "assign",
|
||||
from: "lead",
|
||||
to: "coder",
|
||||
subject: "Build login UI",
|
||||
body: "Please implement the login form and report back.",
|
||||
appearsAt: 1,
|
||||
consumedAt: 2,
|
||||
},
|
||||
{
|
||||
id: "result",
|
||||
from: "coder",
|
||||
to: "reviewer",
|
||||
subject: "Login UI done",
|
||||
body: "Files changed, ready for review.",
|
||||
appearsAt: 4,
|
||||
consumedAt: 5,
|
||||
},
|
||||
{
|
||||
id: "feedback",
|
||||
from: "reviewer",
|
||||
to: "lead",
|
||||
subject: "Review passed",
|
||||
body: "No blockers. One small polish note.",
|
||||
appearsAt: 5,
|
||||
},
|
||||
];
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
title: "A Team Is Mailboxes",
|
||||
desc: "Each agent has its own inbox file. The team does not need shared memory to coordinate.",
|
||||
},
|
||||
{
|
||||
title: "Lead Drops a Card",
|
||||
desc: "Assigning work means appending a message to the coder's inbox.",
|
||||
},
|
||||
{
|
||||
title: "Coder Reads Before Thinking",
|
||||
desc: "Before its next model call, the coder drains its inbox and turns messages into context.",
|
||||
},
|
||||
{
|
||||
title: "Coder Works Alone",
|
||||
desc: "The coder now runs its own loop. The lead does not have to hold the full context.",
|
||||
},
|
||||
{
|
||||
title: "Result Becomes Mail",
|
||||
desc: "The coder sends a result card to the reviewer through the same mailbox mechanism.",
|
||||
},
|
||||
{
|
||||
title: "Reviewer Sends Feedback",
|
||||
desc: "Review feedback is just another card. The lead reads it from its inbox.",
|
||||
},
|
||||
{
|
||||
title: "Files Are the Coordination Layer",
|
||||
desc: "The whole team is inspectable as append-only inbox files: lead.jsonl, coder.jsonl, reviewer.jsonl.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
function visibleMail(agent: AgentId, step: number) {
|
||||
return MAIL.filter((mail) => mail.to === agent && mail.appearsAt <= step && (mail.consumedAt === undefined || step < mail.consumedAt));
|
||||
}
|
||||
|
||||
function agentState(agent: AgentId, step: number): "waiting" | "reading" | "working" | "reviewing" | "done" {
|
||||
if (agent === "lead" && step === 1) return "working";
|
||||
if (agent === "coder" && step === 2) return "reading";
|
||||
if (agent === "coder" && (step === 3 || step === 4)) return "working";
|
||||
if (agent === "reviewer" && step === 5) return "reviewing";
|
||||
if (agent === "lead" && step >= 5) return "reading";
|
||||
if (step === 6) return "done";
|
||||
return "waiting";
|
||||
}
|
||||
|
||||
function stateClass(state: ReturnType<typeof agentState>) {
|
||||
if (state === "working") return "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30";
|
||||
if (state === "reading" || state === "reviewing") return "border-amber-300 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/30";
|
||||
if (state === "done") return "border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30";
|
||||
return "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900";
|
||||
}
|
||||
|
||||
function MailCard({ mail }: { mail: Mail }) {
|
||||
return (
|
||||
<motion.div
|
||||
layout
|
||||
initial={{ opacity: 0, y: 8, scale: 0.98 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: -6, scale: 0.98 }}
|
||||
transition={{ duration: 0.22 }}
|
||||
className="rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-900 shadow-sm dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-100"
|
||||
>
|
||||
<div className="mb-1 flex items-center justify-between gap-2">
|
||||
<span className="font-mono text-[11px] font-semibold">{mail.from} -> {mail.to}</span>
|
||||
<MessageSquareText size={14} />
|
||||
</div>
|
||||
<div className="text-sm font-semibold leading-snug">{mail.subject}</div>
|
||||
<div className="mt-1 text-xs leading-relaxed opacity-85">{mail.body}</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function AgentPanel({ agent, step }: { agent: (typeof AGENTS)[number]; step: number }) {
|
||||
const state = agentState(agent.id, step);
|
||||
const inbox = visibleMail(agent.id, step);
|
||||
|
||||
return (
|
||||
<div className={cn("rounded-lg border p-3 transition-colors", stateClass(state))}>
|
||||
<div className="mb-3 flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<div className="text-base font-bold text-zinc-900 dark:text-zinc-100">{agent.label}</div>
|
||||
<div className="text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">{agent.role}</div>
|
||||
</div>
|
||||
<span className="rounded-md bg-white px-2 py-1 text-[11px] font-semibold capitalize text-zinc-600 shadow-sm dark:bg-zinc-900 dark:text-zinc-300">
|
||||
{state}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="mb-2 flex items-center gap-2 text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<Inbox size={15} />
|
||||
{agent.id}.jsonl
|
||||
</div>
|
||||
<div className="min-h-[118px] space-y-2">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{inbox.length > 0 ? (
|
||||
inbox.map((mail) => <MailCard key={mail.id} mail={mail} />)
|
||||
) : (
|
||||
<motion.div
|
||||
key="empty"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="rounded-md border border-dashed border-zinc-300 px-3 py-8 text-center text-xs text-zinc-500 dark:border-zinc-700 dark:text-zinc-400"
|
||||
>
|
||||
inbox empty
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ActivityLog({ step }: { step: number }) {
|
||||
const items = [
|
||||
"team config creates lead, coder, reviewer",
|
||||
"lead appends task card to coder.jsonl",
|
||||
"coder drains inbox before model call",
|
||||
"coder works in its own loop",
|
||||
"coder appends result to reviewer.jsonl",
|
||||
"reviewer appends feedback to lead.jsonl",
|
||||
"all coordination remains visible on disk",
|
||||
].slice(0, step + 1);
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-800/70">
|
||||
<div className="mb-2 flex items-center gap-2 text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<UsersRound size={16} />
|
||||
What changed
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{items.map((item) => (
|
||||
<motion.div
|
||||
key={item}
|
||||
initial={{ opacity: 0, x: 8 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200"
|
||||
>
|
||||
{item}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AgentTeams({ title }: { title?: string }) {
|
||||
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2500 });
|
||||
const step = vis.currentStep;
|
||||
const current = STEPS[step];
|
||||
|
||||
return (
|
||||
<section className="min-h-[500px] space-y-4">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{title || "Agent Team Mailboxes"}
|
||||
</h2>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="grid gap-3 xl:grid-cols-[1fr_1fr_1fr_0.9fr]">
|
||||
{AGENTS.map((agent) => (
|
||||
<AgentPanel key={agent.id} agent={agent} step={step} />
|
||||
))}
|
||||
<ActivityLog step={step} />
|
||||
</div>
|
||||
|
||||
<StepControls
|
||||
className="mt-4"
|
||||
currentStep={vis.currentStep}
|
||||
totalSteps={vis.totalSteps}
|
||||
onPrev={vis.prev}
|
||||
onNext={vis.next}
|
||||
onReset={vis.reset}
|
||||
isPlaying={vis.isPlaying}
|
||||
onToggleAutoPlay={vis.toggleAutoPlay}
|
||||
stepTitle={current.title}
|
||||
stepDescription={current.desc}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const MEMORY_FILES: MemoryFile[] = [
|
||||
title: "Verification commands",
|
||||
filename: "lcc_test_commands.md",
|
||||
description: "Useful smoke checks for the course website.",
|
||||
body: "Run npm run build, then browser-check /zh/s09 and /zh/s20.",
|
||||
body: "Run npm run build, then browser-check /zh/s09 and /zh/s19.",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { ArrowRight, CheckCircle2, ClipboardCheck, FileText, LockKeyhole, UserCheck } from "lucide-react";
|
||||
import { StepControls } from "@/components/visualizations/shared/step-controls";
|
||||
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Protocol = "shutdown" | "plan";
|
||||
|
||||
const REQUEST_ID = "req_abc";
|
||||
|
||||
const SHUTDOWN_STEPS = [
|
||||
{
|
||||
title: "Agree on a Small Form",
|
||||
desc: "A protocol is just a shared card shape: request type, request_id, and the expected answer.",
|
||||
},
|
||||
{
|
||||
title: "Leader Files a Request",
|
||||
desc: "The leader writes a shutdown request card instead of force-stopping the teammate.",
|
||||
},
|
||||
{
|
||||
title: "Teammate Chooses",
|
||||
desc: "The teammate can approve or reject, and the request_id keeps the answer attached to the right request.",
|
||||
},
|
||||
{
|
||||
title: "Clean Exit",
|
||||
desc: "The approved response returns to the leader, and the teammate exits cleanly.",
|
||||
},
|
||||
];
|
||||
|
||||
const PLAN_STEPS = [
|
||||
{
|
||||
title: "Work Is Locked",
|
||||
desc: "In plan mode, implementation stays locked until a plan card is approved.",
|
||||
},
|
||||
{
|
||||
title: "Submit the Plan Card",
|
||||
desc: "The teammate sends a concrete plan with the same request-response shape.",
|
||||
},
|
||||
{
|
||||
title: "Approval Unlocks Action",
|
||||
desc: "The leader approves the card, then implementation can begin.",
|
||||
},
|
||||
];
|
||||
|
||||
const PROTOCOL_STATES: Record<Protocol, { label: string; detail: string }[]> = {
|
||||
shutdown: [
|
||||
{ label: "drafted", detail: "Lead creates request_id" },
|
||||
{ label: "pending", detail: "card waits in inbox" },
|
||||
{ label: "deciding", detail: "teammate replies" },
|
||||
{ label: "closed", detail: "Lead matches response" },
|
||||
],
|
||||
plan: [
|
||||
{ label: "locked", detail: "work cannot start" },
|
||||
{ label: "submitted", detail: "plan card is sent" },
|
||||
{ label: "approved", detail: "implementation unlocks" },
|
||||
],
|
||||
};
|
||||
|
||||
function ToggleButton({
|
||||
active,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
active: boolean;
|
||||
onClick: () => void;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
"rounded-md px-3 py-1.5 text-xs font-medium transition-colors active:scale-95",
|
||||
active
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-zinc-100 text-zinc-600 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function StateRail({
|
||||
states,
|
||||
currentStep,
|
||||
}: {
|
||||
states: { label: string; detail: string }[];
|
||||
currentStep: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-4 rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-800/70">
|
||||
<div className="mb-3 flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
Protocol state
|
||||
</div>
|
||||
<div className="break-words font-mono text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
request_id: {REQUEST_ID}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-stretch">
|
||||
{states.map((state, index) => {
|
||||
const active = index === currentStep;
|
||||
const done = index < currentStep;
|
||||
return (
|
||||
<div key={state.label} className="flex min-w-0 flex-1 items-stretch gap-2">
|
||||
<motion.div
|
||||
layout
|
||||
animate={active ? { y: [0, -2, 0] } : { y: 0 }}
|
||||
transition={{ duration: 0.8, repeat: active ? Infinity : 0 }}
|
||||
className={cn(
|
||||
"min-w-0 flex-1 rounded-md border px-3 py-2 transition-colors",
|
||||
active
|
||||
? "border-blue-300 bg-blue-50 text-blue-800 dark:border-blue-800 dark:bg-blue-950/35 dark:text-blue-200"
|
||||
: done
|
||||
? "border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/30 dark:text-emerald-200"
|
||||
: "border-zinc-200 bg-white text-zinc-500 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400"
|
||||
)}
|
||||
>
|
||||
<div className="break-words text-sm font-semibold">{state.label}</div>
|
||||
<div className="mt-1 break-words text-[11px] leading-snug opacity-80">
|
||||
{state.detail}
|
||||
</div>
|
||||
</motion.div>
|
||||
{index < states.length - 1 && (
|
||||
<div className="hidden items-center text-zinc-300 dark:text-zinc-600 sm:flex">
|
||||
<ArrowRight size={15} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Desk({
|
||||
title,
|
||||
icon,
|
||||
active,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
icon: ReactNode;
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"min-h-[260px] rounded-lg border p-3 transition-colors",
|
||||
active
|
||||
? "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30"
|
||||
: "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
|
||||
)}
|
||||
>
|
||||
<div className="mb-3 flex min-w-0 items-center gap-2 text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-7 w-7 items-center justify-center rounded-md",
|
||||
active
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300"
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
<span className="min-w-0 break-words">{title}</span>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ProtocolCard({
|
||||
title,
|
||||
rows,
|
||||
tone = "blue",
|
||||
}: {
|
||||
title: string;
|
||||
rows: string[];
|
||||
tone?: "blue" | "amber" | "emerald" | "zinc";
|
||||
}) {
|
||||
const toneClass = {
|
||||
blue: "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-200",
|
||||
amber: "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200",
|
||||
emerald:
|
||||
"border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-200",
|
||||
zinc: "border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200",
|
||||
}[tone];
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
layout
|
||||
initial={{ opacity: 0, y: 10, scale: 0.98 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: -8, scale: 0.98 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className={cn("rounded-md border p-3 shadow-sm", toneClass)}
|
||||
>
|
||||
<div className="break-words font-mono text-xs font-semibold">{title}</div>
|
||||
<div className="mt-2 space-y-1 font-mono text-[11px] opacity-85">
|
||||
{rows.map((row) => (
|
||||
<div key={row} className="break-words">
|
||||
{row}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function EmptyTray({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="rounded-md border border-dashed border-zinc-300 px-3 py-5 text-center text-xs text-zinc-500 dark:border-zinc-700 dark:text-zinc-400">
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TeamProtocols({ title }: { title?: string }) {
|
||||
const [protocol, setProtocol] = useState<Protocol>("shutdown");
|
||||
const steps = protocol === "shutdown" ? SHUTDOWN_STEPS : PLAN_STEPS;
|
||||
const vis = useSteppedVisualization({ totalSteps: steps.length, autoPlayInterval: 2500 });
|
||||
const step = vis.currentStep;
|
||||
|
||||
const switchProtocol = (value: Protocol) => {
|
||||
setProtocol(value);
|
||||
vis.reset();
|
||||
};
|
||||
|
||||
const isPlan = protocol === "plan";
|
||||
|
||||
return (
|
||||
<section className="min-h-[500px] space-y-4">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{title || "Team Protocol Cards"}
|
||||
</h2>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="mb-4 flex justify-center gap-2">
|
||||
<ToggleButton active={!isPlan} onClick={() => switchProtocol("shutdown")}>
|
||||
Shutdown
|
||||
</ToggleButton>
|
||||
<ToggleButton active={isPlan} onClick={() => switchProtocol("plan")}>
|
||||
Plan Approval
|
||||
</ToggleButton>
|
||||
</div>
|
||||
|
||||
<StateRail states={PROTOCOL_STATES[protocol]} currentStep={step} />
|
||||
|
||||
<div className="grid gap-3 lg:grid-cols-3">
|
||||
<Desk
|
||||
title="Leader desk"
|
||||
icon={<UserCheck size={15} />}
|
||||
active={(!isPlan && (step === 1 || step === 3)) || (isPlan && step === 2)}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{!isPlan && step >= 1 && (
|
||||
<ProtocolCard
|
||||
key="shutdown-request"
|
||||
title="shutdown_request"
|
||||
rows={[`request_id: ${REQUEST_ID}`, "target: teammate", "mode: polite"]}
|
||||
tone={step >= 3 ? "zinc" : "blue"}
|
||||
/>
|
||||
)}
|
||||
{!isPlan && step >= 3 && (
|
||||
<ProtocolCard
|
||||
key="shutdown-response"
|
||||
title="shutdown_response"
|
||||
rows={[`request_id: ${REQUEST_ID}`, "approve: true", "status: closed"]}
|
||||
tone="emerald"
|
||||
/>
|
||||
)}
|
||||
{isPlan && step >= 2 && (
|
||||
<ProtocolCard
|
||||
key="plan-approved"
|
||||
title="plan_approval_response"
|
||||
rows={[`request_id: ${REQUEST_ID}`, "approve: true", "unlock: implementation"]}
|
||||
tone="emerald"
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{((!isPlan && step === 0) || (isPlan && step < 2)) && (
|
||||
<EmptyTray label="waiting for a protocol card" />
|
||||
)}
|
||||
</div>
|
||||
</Desk>
|
||||
|
||||
<Desk
|
||||
title="Shared card shape"
|
||||
icon={<ClipboardCheck size={15} />}
|
||||
active={(!isPlan && step === 0) || (isPlan && step === 0)}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<ProtocolCard
|
||||
title="protocol fields"
|
||||
rows={["type", "request_id", "payload", "response"]}
|
||||
tone="amber"
|
||||
/>
|
||||
<div className="rounded-md border border-zinc-200 bg-zinc-50 px-3 py-2 text-xs text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
The key idea is correlation, not ceremony.
|
||||
</div>
|
||||
{isPlan && (
|
||||
<div className="flex items-center gap-2 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs text-zinc-600 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300">
|
||||
<LockKeyhole size={14} />
|
||||
implementation locked until approval
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Desk>
|
||||
|
||||
<Desk
|
||||
title="Teammate desk"
|
||||
icon={isPlan ? <FileText size={15} /> : <CheckCircle2 size={15} />}
|
||||
active={(!isPlan && step === 2) || (isPlan && step === 1)}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{!isPlan && step >= 2 && (
|
||||
<ProtocolCard
|
||||
key="teammate-decision"
|
||||
title="decision card"
|
||||
rows={[`request_id: ${REQUEST_ID}`, "choice: approve", step >= 3 ? "state: exited" : "state: deciding"]}
|
||||
tone={step >= 3 ? "emerald" : "amber"}
|
||||
/>
|
||||
)}
|
||||
{isPlan && step >= 1 && (
|
||||
<ProtocolCard
|
||||
key="plan-card"
|
||||
title="exit_plan_mode"
|
||||
rows={[`request_id: ${REQUEST_ID}`, "1. edit module", "2. run tests", "3. report diff"]}
|
||||
tone={step >= 2 ? "emerald" : "blue"}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{((!isPlan && step < 2) || (isPlan && step === 0)) && (
|
||||
<EmptyTray label={isPlan ? "draft plan not submitted" : "no request received"} />
|
||||
)}
|
||||
</div>
|
||||
</Desk>
|
||||
</div>
|
||||
|
||||
<StepControls
|
||||
className="mt-4"
|
||||
currentStep={vis.currentStep}
|
||||
totalSteps={vis.totalSteps}
|
||||
onPrev={vis.prev}
|
||||
onNext={vis.next}
|
||||
onReset={vis.reset}
|
||||
isPlaying={vis.isPlaying}
|
||||
onToggleAutoPlay={vis.toggleAutoPlay}
|
||||
stepTitle={steps[step].title}
|
||||
stepDescription={steps[step].desc}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { CheckCircle2, ClipboardList, Hourglass, UserRoundCog } from "lucide-react";
|
||||
import { StepControls } from "@/components/visualizations/shared/step-controls";
|
||||
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type AgentPhase = "idle" | "polling" | "claiming" | "working" | "done";
|
||||
type TaskStatus = "open" | "claimed" | "complete";
|
||||
|
||||
interface AgentState {
|
||||
id: string;
|
||||
phase: AgentPhase;
|
||||
timer: number;
|
||||
task?: string;
|
||||
}
|
||||
|
||||
interface TaskState {
|
||||
id: string;
|
||||
title: string;
|
||||
status: TaskStatus;
|
||||
owner?: string;
|
||||
}
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
title: "Quiet Agents",
|
||||
desc: "Autonomous agents start by waiting. The important mental model is a work board, not a central dispatcher.",
|
||||
},
|
||||
{
|
||||
title: "Idle Timer Fills",
|
||||
desc: "An agent watches its own idle timer. When it waits long enough, it decides to look for work.",
|
||||
},
|
||||
{
|
||||
title: "Read the Board",
|
||||
desc: "The agent polls the shared task board and looks for an open card.",
|
||||
},
|
||||
{
|
||||
title: "Claim One Card",
|
||||
desc: "Claiming writes the agent name onto one task, making ownership visible.",
|
||||
},
|
||||
{
|
||||
title: "Work Independently",
|
||||
desc: "The claimed task moves into the agent workspace. No coordinator has to babysit it.",
|
||||
},
|
||||
{
|
||||
title: "Others Join In",
|
||||
desc: "A second agent can claim a different card through the same simple habit.",
|
||||
},
|
||||
{
|
||||
title: "Finish and Free Up",
|
||||
desc: "Completed work goes back to the board as done, and the agent returns to waiting.",
|
||||
},
|
||||
{
|
||||
title: "Self Organization",
|
||||
desc: "Timers plus visible ownership let a small group organize itself without a manager loop.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
const TASKS = [
|
||||
{ id: "T1", title: "Fix auth bug" },
|
||||
{ id: "T2", title: "Add rate limiter" },
|
||||
{ id: "T3", title: "Write docs" },
|
||||
{ id: "T4", title: "Clean tests" },
|
||||
];
|
||||
|
||||
function getAgents(step: number): AgentState[] {
|
||||
if (step === 0) {
|
||||
return [
|
||||
{ id: "A", phase: "idle", timer: 0.1 },
|
||||
{ id: "B", phase: "idle", timer: 0 },
|
||||
{ id: "C", phase: "idle", timer: 0 },
|
||||
];
|
||||
}
|
||||
if (step === 1) {
|
||||
return [
|
||||
{ id: "A", phase: "idle", timer: 0.85 },
|
||||
{ id: "B", phase: "idle", timer: 0.25 },
|
||||
{ id: "C", phase: "idle", timer: 0 },
|
||||
];
|
||||
}
|
||||
if (step === 2) {
|
||||
return [
|
||||
{ id: "A", phase: "polling", timer: 1 },
|
||||
{ id: "B", phase: "idle", timer: 0.25 },
|
||||
{ id: "C", phase: "idle", timer: 0 },
|
||||
];
|
||||
}
|
||||
if (step === 3) {
|
||||
return [
|
||||
{ id: "A", phase: "claiming", timer: 0, task: "T1" },
|
||||
{ id: "B", phase: "idle", timer: 0.45 },
|
||||
{ id: "C", phase: "idle", timer: 0.1 },
|
||||
];
|
||||
}
|
||||
if (step === 4) {
|
||||
return [
|
||||
{ id: "A", phase: "working", timer: 0, task: "T1" },
|
||||
{ id: "B", phase: "idle", timer: 0.65 },
|
||||
{ id: "C", phase: "idle", timer: 0.2 },
|
||||
];
|
||||
}
|
||||
if (step === 5) {
|
||||
return [
|
||||
{ id: "A", phase: "working", timer: 0, task: "T1" },
|
||||
{ id: "B", phase: "claiming", timer: 0, task: "T2" },
|
||||
{ id: "C", phase: "idle", timer: 0.35 },
|
||||
];
|
||||
}
|
||||
if (step === 6) {
|
||||
return [
|
||||
{ id: "A", phase: "done", timer: 0, task: "T1" },
|
||||
{ id: "B", phase: "working", timer: 0, task: "T2" },
|
||||
{ id: "C", phase: "idle", timer: 0.6 },
|
||||
];
|
||||
}
|
||||
return [
|
||||
{ id: "A", phase: "idle", timer: 0.15 },
|
||||
{ id: "B", phase: "working", timer: 0, task: "T2" },
|
||||
{ id: "C", phase: "claiming", timer: 0, task: "T3" },
|
||||
];
|
||||
}
|
||||
|
||||
function getTasks(step: number): TaskState[] {
|
||||
return TASKS.map((task) => {
|
||||
if (task.id === "T1" && step >= 6) {
|
||||
return { ...task, status: "complete", owner: "A" };
|
||||
}
|
||||
if (task.id === "T1" && step >= 3) {
|
||||
return { ...task, status: "claimed", owner: "A" };
|
||||
}
|
||||
if (task.id === "T2" && step >= 5) {
|
||||
return { ...task, status: "claimed", owner: "B" };
|
||||
}
|
||||
if (task.id === "T3" && step >= 7) {
|
||||
return { ...task, status: "claimed", owner: "C" };
|
||||
}
|
||||
return { ...task, status: "open" };
|
||||
});
|
||||
}
|
||||
|
||||
function phaseClass(phase: AgentPhase): string {
|
||||
if (phase === "working") return "border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30";
|
||||
if (phase === "claiming" || phase === "polling") return "border-amber-300 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/30";
|
||||
if (phase === "done") return "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30";
|
||||
return "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900";
|
||||
}
|
||||
|
||||
function statusClass(status: TaskStatus): string {
|
||||
if (status === "complete") return "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300";
|
||||
if (status === "claimed") return "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300";
|
||||
return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300";
|
||||
}
|
||||
|
||||
function AgentCard({ agent }: { agent: AgentState }) {
|
||||
const timerPercent = Math.round(agent.timer * 100);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
layout
|
||||
animate={agent.phase !== "idle" ? { y: [0, -2, 0] } : { y: 0 }}
|
||||
transition={{ duration: 0.9, repeat: agent.phase !== "idle" ? Infinity : 0 }}
|
||||
className={cn("rounded-lg border p-3 transition-colors", phaseClass(agent.phase))}
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-md bg-zinc-900 text-sm font-bold text-white dark:bg-zinc-100 dark:text-zinc-900">
|
||||
{agent.id}
|
||||
</span>
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-zinc-800 dark:text-zinc-100">Agent {agent.id}</div>
|
||||
<div className="text-[11px] capitalize text-zinc-500 dark:text-zinc-400">{agent.phase}</div>
|
||||
</div>
|
||||
</div>
|
||||
{agent.phase === "done" ? (
|
||||
<CheckCircle2 size={18} className="text-emerald-500" />
|
||||
) : (
|
||||
<UserRoundCog size={18} className="text-zinc-400" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-2 h-2 overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<motion.div
|
||||
className="h-full rounded-full bg-amber-400"
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${timerPercent}%` }}
|
||||
transition={{ duration: 0.35 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="font-mono text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
{agent.task ? `task: ${agent.task}` : `idle timer: ${timerPercent}%`}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AutonomousAgents({ title }: { title?: string }) {
|
||||
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2500 });
|
||||
const step = vis.currentStep;
|
||||
const agents = getAgents(step);
|
||||
const tasks = getTasks(step);
|
||||
const current = STEPS[step];
|
||||
|
||||
return (
|
||||
<section className="min-h-[500px] space-y-4">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{title || "Autonomous Work Board"}
|
||||
</h2>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="grid gap-3 lg:grid-cols-[1fr_1.2fr]">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<Hourglass size={16} />
|
||||
Agents watch their own idle timer
|
||||
</div>
|
||||
<div className="grid gap-2 sm:grid-cols-3 lg:grid-cols-1">
|
||||
{agents.map((agent) => (
|
||||
<AgentCard key={`${agent.id}-${agent.phase}-${agent.task ?? "none"}-${step}`} agent={agent} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-800/70">
|
||||
<div className="mb-3 flex items-center gap-2 text-sm font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<ClipboardList size={16} />
|
||||
Shared task board
|
||||
</div>
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{tasks.map((task) => (
|
||||
<motion.div
|
||||
layout
|
||||
key={`${task.id}-${task.status}-${task.owner ?? "open"}`}
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.96 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="rounded-md border border-zinc-200 bg-white p-3 text-xs shadow-sm dark:border-zinc-700 dark:bg-zinc-900"
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<span className="font-mono font-semibold text-zinc-500 dark:text-zinc-400">{task.id}</span>
|
||||
<span className={cn("rounded px-1.5 py-0.5 text-[10px] font-semibold", statusClass(task.status))}>
|
||||
{task.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-medium text-zinc-800 dark:text-zinc-100">{task.title}</div>
|
||||
<div className="mt-2 font-mono text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
owner: {task.owner ?? "-"}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div className="mt-3 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs text-zinc-600 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300">
|
||||
Nobody assigns tasks directly; agents claim visible open cards when their timers wake them.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StepControls
|
||||
className="mt-4"
|
||||
currentStep={vis.currentStep}
|
||||
totalSteps={vis.totalSteps}
|
||||
onPrev={vis.prev}
|
||||
onNext={vis.next}
|
||||
onReset={vis.reset}
|
||||
isPlaying={vis.isPlaying}
|
||||
onToggleAutoPlay={vis.toggleAutoPlay}
|
||||
stepTitle={current.title}
|
||||
stepDescription={current.desc}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
|
||||
import { StepControls } from "@/components/visualizations/shared/step-controls";
|
||||
|
||||
type TaskStatus = "pending" | "in_progress" | "completed";
|
||||
|
||||
interface TaskRow {
|
||||
id: number;
|
||||
subject: string;
|
||||
status: TaskStatus;
|
||||
worktree: string;
|
||||
}
|
||||
|
||||
interface WorktreeRow {
|
||||
name: string;
|
||||
branch: string;
|
||||
task: string;
|
||||
state: "none" | "active" | "kept" | "removed";
|
||||
}
|
||||
|
||||
interface Lane {
|
||||
name: string;
|
||||
files: string[];
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
interface StepState {
|
||||
title: string;
|
||||
desc: string;
|
||||
tasks: TaskRow[];
|
||||
worktrees: WorktreeRow[];
|
||||
lanes: Lane[];
|
||||
op: string;
|
||||
}
|
||||
|
||||
const STEPS: StepState[] = [
|
||||
{
|
||||
title: "Single Workspace Pain",
|
||||
desc: "Two tasks are active, but both edits would hit one directory and collide.",
|
||||
op: "task_create x2",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "in_progress", worktree: "" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "" },
|
||||
],
|
||||
worktrees: [],
|
||||
lanes: [
|
||||
{ name: "main", files: ["auth/service.py", "ui/Login.tsx"], highlight: true },
|
||||
{ name: "wt/auth-refactor", files: [] },
|
||||
{ name: "wt/ui-login", files: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Allocate Lane for Task 1",
|
||||
desc: "Create a worktree lane and associate it with task 1 for clear ownership.",
|
||||
op: "worktree_create(name='auth-refactor', task_id=1)",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "in_progress", worktree: "auth-refactor" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "" },
|
||||
],
|
||||
worktrees: [
|
||||
{ name: "auth-refactor", branch: "wt/auth-refactor", task: "#1", state: "active" },
|
||||
],
|
||||
lanes: [
|
||||
{ name: "main", files: ["ui/Login.tsx"] },
|
||||
{ name: "wt/auth-refactor", files: ["auth/service.py"], highlight: true },
|
||||
{ name: "wt/ui-login", files: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Allocate Lane for Task 2",
|
||||
desc: "Lane creation and task association can be separate. Here task 2 binds after lane creation.",
|
||||
op: "worktree_create(name='ui-login')\ntask_bind_worktree(task_id=2, worktree='ui-login')",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "in_progress", worktree: "auth-refactor" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "ui-login" },
|
||||
],
|
||||
worktrees: [
|
||||
{ name: "auth-refactor", branch: "wt/auth-refactor", task: "#1", state: "active" },
|
||||
{ name: "ui-login", branch: "wt/ui-login", task: "#2", state: "active" },
|
||||
],
|
||||
lanes: [
|
||||
{ name: "main", files: [] },
|
||||
{ name: "wt/auth-refactor", files: ["auth/service.py"] },
|
||||
{ name: "wt/ui-login", files: ["ui/Login.tsx"], highlight: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Run Commands in Isolated Lanes",
|
||||
desc: "Each command routes by selected lane directory, not by the shared root.",
|
||||
op: "worktree_run('auth-refactor', 'pytest tests/auth -q')",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "in_progress", worktree: "auth-refactor" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "ui-login" },
|
||||
],
|
||||
worktrees: [
|
||||
{ name: "auth-refactor", branch: "wt/auth-refactor", task: "#1", state: "active" },
|
||||
{ name: "ui-login", branch: "wt/ui-login", task: "#2", state: "active" },
|
||||
],
|
||||
lanes: [
|
||||
{ name: "main", files: [] },
|
||||
{ name: "wt/auth-refactor", files: ["auth/service.py", "tests/auth/test_login.py"], highlight: true },
|
||||
{ name: "wt/ui-login", files: ["ui/Login.tsx", "ui/Login.css"] },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Keep One Lane, Close Another",
|
||||
desc: "Closeout can mix decisions: keep ui-login active for follow-up, remove auth-refactor and complete task 1.",
|
||||
op: "worktree_keep('ui-login')\nworktree_remove('auth-refactor', complete_task=true)\nworktree_events(limit=10)",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "completed", worktree: "" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "ui-login" },
|
||||
],
|
||||
worktrees: [
|
||||
{ name: "auth-refactor", branch: "wt/auth-refactor", task: "#1", state: "removed" },
|
||||
{ name: "ui-login", branch: "wt/ui-login", task: "#2", state: "kept" },
|
||||
],
|
||||
lanes: [
|
||||
{ name: "main", files: [] },
|
||||
{ name: "wt/auth-refactor", files: [] },
|
||||
{ name: "wt/ui-login", files: ["ui/Login.tsx"], highlight: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Isolation + Coordination + Events",
|
||||
desc: "The board tracks shared truth, worktree lanes isolate execution, and events provide auditable side-channel traces.",
|
||||
op: "task_list + worktree_list + worktree_events",
|
||||
tasks: [
|
||||
{ id: 1, subject: "Auth refactor", status: "completed", worktree: "" },
|
||||
{ id: 2, subject: "UI login polish", status: "in_progress", worktree: "ui-login" },
|
||||
],
|
||||
worktrees: [
|
||||
{ name: "auth-refactor", branch: "wt/auth-refactor", task: "#1", state: "removed" },
|
||||
{ name: "ui-login", branch: "wt/ui-login", task: "#2", state: "kept" },
|
||||
],
|
||||
lanes: [
|
||||
{ name: "main", files: [] },
|
||||
{ name: "wt/auth-refactor", files: [] },
|
||||
{ name: "wt/ui-login", files: ["ui/Login.tsx"], highlight: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function statusClass(status: TaskStatus): string {
|
||||
if (status === "completed") return "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300";
|
||||
if (status === "in_progress") return "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300";
|
||||
return "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300";
|
||||
}
|
||||
|
||||
function worktreeClass(state: WorktreeRow["state"]): string {
|
||||
if (state === "active") return "border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-900/20";
|
||||
if (state === "kept") return "border-sky-300 bg-sky-50 dark:border-sky-800 dark:bg-sky-900/20";
|
||||
if (state === "removed") return "border-zinc-200 bg-zinc-100 opacity-70 dark:border-zinc-700 dark:bg-zinc-800";
|
||||
return "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900";
|
||||
}
|
||||
|
||||
export default function WorktreeTaskIsolation({ title }: { title?: string }) {
|
||||
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2600 });
|
||||
const step = STEPS[vis.currentStep];
|
||||
|
||||
return (
|
||||
<section className="min-h-[500px] space-y-4">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{title || "Worktree Task Isolation"}
|
||||
</h2>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="mb-3 rounded-md border border-blue-200 bg-blue-50 px-3 py-2 font-mono text-xs text-blue-700 dark:border-blue-900 dark:bg-blue-950/30 dark:text-blue-300">
|
||||
{step.op}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 lg:grid-cols-3">
|
||||
<div className="rounded-md border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="border-b border-zinc-200 bg-zinc-50 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
Task Board (.tasks)
|
||||
</div>
|
||||
<div className="space-y-2 p-2">
|
||||
{step.tasks.map((task) => (
|
||||
<motion.div
|
||||
key={`${task.id}-${task.status}-${task.worktree}`}
|
||||
initial={{ opacity: 0, y: 6 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className="rounded border border-zinc-200 p-2 text-xs dark:border-zinc-700"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="font-mono text-zinc-500 dark:text-zinc-400">#{task.id}</span>
|
||||
<span className={`rounded px-1.5 py-0.5 text-[10px] font-semibold ${statusClass(task.status)}`}>
|
||||
{task.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 font-medium text-zinc-800 dark:text-zinc-100">{task.subject}</div>
|
||||
<div className="mt-1 font-mono text-[10px] text-zinc-500 dark:text-zinc-400">
|
||||
worktree: {task.worktree || "-"}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="border-b border-zinc-200 bg-zinc-50 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
Worktree Index (.worktrees/index.json)
|
||||
</div>
|
||||
<div className="space-y-2 p-2">
|
||||
{step.worktrees.length === 0 && (
|
||||
<div className="rounded border border-dashed border-zinc-300 px-3 py-4 text-center text-xs text-zinc-500 dark:border-zinc-700 dark:text-zinc-400">
|
||||
no worktrees yet
|
||||
</div>
|
||||
)}
|
||||
{step.worktrees.map((wt) => (
|
||||
<motion.div
|
||||
key={`${wt.name}-${wt.state}`}
|
||||
initial={{ opacity: 0, y: 6 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className={`rounded border p-2 text-xs ${worktreeClass(wt.state)}`}
|
||||
>
|
||||
<div className="font-mono text-[11px] font-semibold text-zinc-800 dark:text-zinc-100">{wt.name}</div>
|
||||
<div className="font-mono text-[10px] text-zinc-500 dark:text-zinc-400">{wt.branch}</div>
|
||||
<div className="mt-1 text-[10px] text-zinc-600 dark:text-zinc-300">task: {wt.task}</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="border-b border-zinc-200 bg-zinc-50 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
Execution Lanes
|
||||
</div>
|
||||
<div className="space-y-2 p-2">
|
||||
{step.lanes.map((lane) => (
|
||||
<motion.div
|
||||
key={`${lane.name}-${lane.files.join(",")}`}
|
||||
initial={{ opacity: 0, x: -4 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className={`rounded border p-2 text-xs ${
|
||||
lane.highlight
|
||||
? "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20"
|
||||
: "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
|
||||
}`}
|
||||
>
|
||||
<div className="font-mono text-[11px] font-semibold text-zinc-800 dark:text-zinc-100">{lane.name}</div>
|
||||
<div className="mt-1 space-y-1 font-mono text-[10px] text-zinc-500 dark:text-zinc-400">
|
||||
{lane.files.length === 0 ? (
|
||||
<div>(no changes)</div>
|
||||
) : (
|
||||
lane.files.map((f) => <div key={f}>{f}</div>)
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-md border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-zinc-800/60">
|
||||
<div className="font-medium text-zinc-800 dark:text-zinc-100">{step.title}</div>
|
||||
<div className="text-zinc-600 dark:text-zinc-300">{step.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StepControls
|
||||
currentStep={vis.currentStep}
|
||||
totalSteps={vis.totalSteps}
|
||||
onPrev={vis.prev}
|
||||
onNext={vis.next}
|
||||
onReset={vis.reset}
|
||||
isPlaying={vis.isPlaying}
|
||||
onToggleAutoPlay={vis.toggleAutoPlay}
|
||||
stepTitle={step.title}
|
||||
stepDescription={step.desc}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
289
web/src/components/visualizations/s15-team-runtime.tsx
Normal file
289
web/src/components/visualizations/s15-team-runtime.tsx
Normal file
@@ -0,0 +1,289 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
CheckCircle2,
|
||||
ClipboardList,
|
||||
GitBranch,
|
||||
Inbox,
|
||||
LockKeyhole,
|
||||
Search,
|
||||
Terminal,
|
||||
UsersRound,
|
||||
} from "lucide-react";
|
||||
import { StepControls } from "@/components/visualizations/shared/step-controls";
|
||||
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
title: "Confirm a Small Team",
|
||||
desc: "The Lead proposes focused roles and waits for the user before starting persistent teammates.",
|
||||
event: "user confirmed: backend + tests",
|
||||
},
|
||||
{
|
||||
title: "Deliver a Typed Assignment",
|
||||
desc: "The runtime writes the assignment to a mailbox and correlates plan approval with a request id.",
|
||||
event: "plan_response(req_7, approved=true)",
|
||||
},
|
||||
{
|
||||
title: "Idle Teammates Scan the Board",
|
||||
desc: "A teammate with no direct message looks only for pending, unowned work whose dependencies are complete.",
|
||||
event: "scan_ready_tasks(backend) -> task_auth",
|
||||
},
|
||||
{
|
||||
title: "Claim Under One Lock",
|
||||
desc: "Ownership and status change atomically, so another teammate cannot take the same task.",
|
||||
event: "task_lock: task_auth -> backend",
|
||||
},
|
||||
{
|
||||
title: "Route Tools to the Task Directory",
|
||||
desc: "The claimed task carries its worktree binding; bash, read, and write derive their cwd from that record.",
|
||||
event: "cwd -> .worktrees/auth-refactor",
|
||||
},
|
||||
{
|
||||
title: "Return the Result, Keep the Teammate",
|
||||
desc: "The runtime delivers the result to the Lead and moves the teammate back to IDLE for newly ready work.",
|
||||
event: "result(auth complete) -> idle_notification",
|
||||
},
|
||||
] as const;
|
||||
|
||||
const EVENTS = STEPS.map((step) => step.event);
|
||||
|
||||
function StateBadge({
|
||||
label,
|
||||
tone,
|
||||
}: {
|
||||
label: string;
|
||||
tone: "zinc" | "blue" | "amber" | "emerald";
|
||||
}) {
|
||||
const classes = {
|
||||
zinc: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",
|
||||
blue: "bg-blue-100 text-blue-700 dark:bg-blue-950/50 dark:text-blue-200",
|
||||
amber: "bg-amber-100 text-amber-700 dark:bg-amber-950/50 dark:text-amber-200",
|
||||
emerald:
|
||||
"bg-emerald-100 text-emerald-700 dark:bg-emerald-950/50 dark:text-emerald-200",
|
||||
}[tone];
|
||||
|
||||
return (
|
||||
<span className={cn("shrink-0 whitespace-nowrap rounded px-2 py-1 text-[11px] font-semibold", classes)}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function RuntimePanel({ step }: { step: number }) {
|
||||
const state =
|
||||
step === 0 ? "awaiting confirmation" : step === 1 ? "working" : step === 5 ? "idle" : "active";
|
||||
const tone = step === 0 ? "zinc" : step === 5 ? "emerald" : "blue";
|
||||
|
||||
return (
|
||||
<div className="min-h-[250px] rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<UsersRound size={16} className="shrink-0 text-blue-500" />
|
||||
<span className="break-words">Team runtime</span>
|
||||
</div>
|
||||
<StateBadge label={state} tone={tone} />
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid grid-cols-[88px_minmax(0,1fr)] gap-x-2 gap-y-3 text-xs">
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Lead</span>
|
||||
<span className="break-words font-medium text-zinc-800 dark:text-zinc-200">
|
||||
{step === 0 ? "proposes roles" : step === 5 ? "receives result" : "coordinates"}
|
||||
</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Teammate</span>
|
||||
<span className="break-words font-medium text-zinc-800 dark:text-zinc-200">
|
||||
backend
|
||||
</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Protocol</span>
|
||||
<span className="break-all font-mono text-zinc-700 dark:text-zinc-300">
|
||||
{step < 1 ? "-" : "request_id=req_7"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex min-h-[72px] items-start gap-2 rounded-md border border-blue-200 bg-blue-50 p-3 text-xs text-blue-800 dark:border-blue-900 dark:bg-blue-950/30 dark:text-blue-200">
|
||||
<Inbox size={15} className="mt-0.5 shrink-0" />
|
||||
<span className="break-words leading-relaxed">
|
||||
{step === 0
|
||||
? "No mailbox is created before confirmation."
|
||||
: step === 1
|
||||
? "Assignment and plan approval travel through typed messages."
|
||||
: step === 5
|
||||
? "The result wakes the Lead; IDLE is a reusable state."
|
||||
: "The runtime owns delivery while the teammate works."}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TaskPanel({ step }: { step: number }) {
|
||||
const status = step < 3 ? "pending" : step < 5 ? "in_progress" : "completed";
|
||||
const owner = step < 3 ? "-" : "backend";
|
||||
const tone = status === "pending" ? "zinc" : status === "in_progress" ? "amber" : "emerald";
|
||||
|
||||
return (
|
||||
<div className="min-h-[250px] rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<ClipboardList size={16} className="shrink-0 text-amber-500" />
|
||||
<span>Task board</span>
|
||||
</div>
|
||||
<StateBadge label={status} tone={tone} />
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-md border border-zinc-200 p-3 dark:border-zinc-700">
|
||||
<div className="font-mono text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
task_auth
|
||||
</div>
|
||||
<div className="mt-1 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
Refactor authentication
|
||||
</div>
|
||||
<div className="mt-3 grid grid-cols-[76px_minmax(0,1fr)] gap-2 text-xs">
|
||||
<span className="text-zinc-500 dark:text-zinc-400">owner</span>
|
||||
<span className="font-mono text-zinc-700 dark:text-zinc-300">{owner}</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">blockedBy</span>
|
||||
<span className="font-mono text-zinc-700 dark:text-zinc-300">[]</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"mt-3 flex min-h-[56px] items-center gap-2 rounded-md border px-3 py-2 text-xs",
|
||||
step === 2 || step === 3
|
||||
? "border-amber-300 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-200"
|
||||
: "border-zinc-200 bg-zinc-50 text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
|
||||
)}
|
||||
>
|
||||
{step < 3 ? <Search size={15} className="shrink-0" /> : <LockKeyhole size={15} className="shrink-0" />}
|
||||
<span className="break-words">
|
||||
{step < 2
|
||||
? "Waiting for the teammate loop."
|
||||
: step === 2
|
||||
? "Ready filter: pending + unowned + dependencies complete."
|
||||
: "The claim check and update share one lock."}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function WorkspacePanel({ step }: { step: number }) {
|
||||
const bound = step >= 4;
|
||||
|
||||
return (
|
||||
<div className="min-h-[250px] rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<GitBranch size={16} className="shrink-0 text-emerald-500" />
|
||||
<span>Task directory</span>
|
||||
</div>
|
||||
<StateBadge label={bound ? "bound" : "reserved"} tone={bound ? "emerald" : "zinc"} />
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-md border p-3 transition-colors",
|
||||
!bound
|
||||
? "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30"
|
||||
: "border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800"
|
||||
)}
|
||||
>
|
||||
<div className="font-mono text-xs font-semibold text-zinc-800 dark:text-zinc-200">
|
||||
repository root
|
||||
</div>
|
||||
<div className="mt-1 text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
coordination state
|
||||
</div>
|
||||
</div>
|
||||
<motion.div
|
||||
animate={bound ? { y: [0, -2, 0] } : { y: 0 }}
|
||||
transition={{ duration: 0.8, repeat: bound ? Infinity : 0 }}
|
||||
className={cn(
|
||||
"rounded-md border p-3 transition-colors",
|
||||
bound
|
||||
? "border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30"
|
||||
: "border-dashed border-zinc-300 bg-white dark:border-zinc-700 dark:bg-zinc-900"
|
||||
)}
|
||||
>
|
||||
<div className="break-all font-mono text-xs font-semibold text-zinc-800 dark:text-zinc-200">
|
||||
.worktrees/auth-refactor
|
||||
</div>
|
||||
<div className="mt-1 text-[11px] text-zinc-500 dark:text-zinc-400">
|
||||
{bound ? "bash / read / write cwd" : "task.worktree binding"}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center gap-2 text-xs text-zinc-600 dark:text-zinc-300">
|
||||
{bound ? <Terminal size={15} /> : <GitBranch size={15} />}
|
||||
<span>{bound ? "Tools follow the claimed task." : "No implicit directory switching."}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TeamRuntime({ title }: { title?: string }) {
|
||||
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2800 });
|
||||
const step = vis.currentStep;
|
||||
const current = STEPS[step];
|
||||
|
||||
return (
|
||||
<section className="min-h-[500px] space-y-4">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{title || "Agent Team Runtime"}
|
||||
</h2>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<div className="grid gap-3 lg:grid-cols-3">
|
||||
<RuntimePanel step={step} />
|
||||
<TaskPanel step={step} />
|
||||
<WorkspacePanel step={step} />
|
||||
</div>
|
||||
|
||||
<div className="mt-3 rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div className="mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
|
||||
<CheckCircle2 size={14} />
|
||||
Runtime events
|
||||
</div>
|
||||
<div className="grid gap-2 md:grid-cols-2 xl:grid-cols-3">
|
||||
{EVENTS.map((event, index) => {
|
||||
const visible = index <= step;
|
||||
return (
|
||||
<motion.div
|
||||
key={event}
|
||||
initial={false}
|
||||
animate={{ opacity: visible ? 1 : 0.18, y: 0 }}
|
||||
aria-hidden={!visible}
|
||||
className={cn(
|
||||
"break-all rounded-md border px-2 py-1.5 font-mono text-[10px]",
|
||||
index === step
|
||||
? "border-blue-300 bg-blue-50 text-blue-700 dark:border-blue-800 dark:bg-blue-950/30 dark:text-blue-200"
|
||||
: "border-zinc-200 bg-zinc-50 text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
|
||||
)}
|
||||
>
|
||||
{event}
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StepControls
|
||||
className="mt-4"
|
||||
currentStep={vis.currentStep}
|
||||
totalSteps={vis.totalSteps}
|
||||
onPrev={vis.prev}
|
||||
onNext={vis.next}
|
||||
onReset={vis.reset}
|
||||
isPlaying={vis.isPlaying}
|
||||
onToggleAutoPlay={vis.toggleAutoPlay}
|
||||
stepTitle={current.title}
|
||||
stepDescription={current.desc}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ const STAGES: {
|
||||
{
|
||||
id: "execute",
|
||||
label: "Execute",
|
||||
detail: "local tools, teams, worktrees",
|
||||
detail: "local tools, teams, task-bound worktrees",
|
||||
icon: <Wrench size={15} />,
|
||||
},
|
||||
{
|
||||
@@ -82,7 +82,7 @@ const STAGES: {
|
||||
const SURFACES = [
|
||||
{ label: "background", icon: <Clock3 size={14} />, text: "slow commands can finish later" },
|
||||
{ label: "team", icon: <Network size={14} />, text: "teammates work through mailboxes" },
|
||||
{ label: "worktree", icon: <GitBranch size={14} />, text: "risky edits stay isolated" },
|
||||
{ label: "worktree", icon: <GitBranch size={14} />, text: "task-bound cwd selects a separate checkout" },
|
||||
{ label: "MCP", icon: <Blocks size={14} />, text: "external tools are normalized" },
|
||||
];
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
{
|
||||
"id": "runtime-owned-delivery",
|
||||
"title": "Message Delivery Belongs to the Runtime",
|
||||
"description": "MessageBus stores each handoff in JSONL, while the runtime watches the Lead mailbox and injects new team events into the next turn. The model does not need an inbox polling tool.",
|
||||
"description": "The MessageBus persists each handoff, while the runtime watches the Lead mailbox and injects new team events into the next turn. The model does not spend turns polling an inbox.",
|
||||
"alternatives": "A model-visible check_inbox tool is easy to add, but wastes turns and can leave completed work unnoticed.",
|
||||
"zh": {
|
||||
"title": "消息投递由运行时负责",
|
||||
"description": "MessageBus 把每次交接写入 JSONL,运行时监听 Lead 邮箱,并把新的团队事件送入下一轮上下文。模型不需要调用邮箱轮询工具。"
|
||||
"description": "MessageBus 持久化每次交接,运行时监听 Lead 邮箱,并把新的团队事件送入下一轮上下文。模型不需要浪费轮次轮询收件箱。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "メッセージ配信はランタイムが担う",
|
||||
"description": "MessageBus は各ハンドオフを JSONL に保存し、ランタイムが Lead のメールボックスを監視して新しい team event を次の turn に注入する。モデルに受信箱確認ツールは要らない。"
|
||||
"description": "MessageBus が各ハンドオフを永続化し、ランタイムが Lead の受信箱を監視して新しい team event を次の turn に注入する。モデルは受信箱のポーリングに turn を費やさない。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -46,15 +46,57 @@
|
||||
{
|
||||
"id": "plan-approval-is-a-gate",
|
||||
"title": "Plan Approval Is an Execution Gate",
|
||||
"description": "When the Lead requests a plan, mutating tools remain blocked until the matching plan is approved. Rejection requires a new submission, and an idle teammate remains available for later assignments until a typed shutdown completes.",
|
||||
"alternatives": "Treating approval as a conversational suggestion cannot prevent an early write or shell command.",
|
||||
"description": "When the Lead requests a plan, mutating tools remain blocked until the matching plan is approved. Rejection requires a new submission rather than a conversational workaround.",
|
||||
"alternatives": "Treating approval as a suggestion cannot prevent an early write or shell command.",
|
||||
"zh": {
|
||||
"title": "计划审批是执行闸门",
|
||||
"description": "Lead 请求计划后,修改类工具会保持阻塞,直到对应计划通过。被拒绝的计划必须重新提交;空闲队友会继续保留,直到类型化关机协议完成。"
|
||||
"description": "Lead 请求计划后,修改类工具会保持阻塞,直到对应计划通过。被拒绝的计划必须重新提交,不能靠对话绕过。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "プラン承認を実行ゲートにする",
|
||||
"description": "Lead がプランを要求すると、対応するプランが承認されるまで変更系ツールをブロックする。却下後は再提出が必要で、待機中のチームメイトは型付き終了プロトコルが完了するまで残る。"
|
||||
"description": "Lead がプランを要求すると、対応するプランが承認されるまで変更系ツールをブロックする。却下後は会話で迂回せず再提出が必要になる。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "idle-claim-is-atomic",
|
||||
"title": "Idle Discovery and Claiming Form One Safe Path",
|
||||
"description": "An idle teammate scans only pending, unowned tasks whose dependencies are complete. The ownership check and pending-to-in_progress update happen under one lock, so two teammates cannot claim the same work.",
|
||||
"alternatives": "Central dispatch keeps assignment simple, while an unlocked scan can assign the same task twice.",
|
||||
"zh": {
|
||||
"title": "空闲发现与原子认领组成一条安全路径",
|
||||
"description": "空闲队友只扫描 pending、未分配且依赖已完成的任务。所有权检查与 pending 到 in_progress 的更新在同一把锁内完成,因此两个队友不会认领同一任务。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "待機中の探索と原子的な認領を一つの安全な経路にする",
|
||||
"description": "待機中のチームメイトは pending、未所有、依存解決済みのタスクだけを探す。所有権確認と pending から in_progress への更新を同じ lock 内で行い、二重認領を防ぐ。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "task-bound-worktree",
|
||||
"title": "The Working Directory Is a Task Property",
|
||||
"description": "A task records its worktree binding, and a teammate's bash, read, and write tools derive their working directory from the claimed task. This is explicit cwd routing for parallel edits, not a security sandbox or a second orchestration system.",
|
||||
"alternatives": "Letting agents switch directories implicitly is shorter, but makes the cwd boundary invisible and easy to lose.",
|
||||
"zh": {
|
||||
"title": "工作目录是任务的显式属性",
|
||||
"description": "任务记录自己的 worktree 绑定,队友的 bash、read、write 工具从已认领任务推导工作目录。这是为并行编辑提供的显式 cwd 路由,不是安全沙箱,也不是第二套编排系统。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "作業ディレクトリをタスクの明示的な属性にする",
|
||||
"description": "タスクが worktree の紐付けを保持し、チームメイトの bash、read、write は認領したタスクから作業ディレクトリを決める。これは並行編集のための明示的な cwd routing であり、security sandbox や第二の編成システムではない。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "worktree-cleanup-fails-closed",
|
||||
"title": "Worktree Cleanup Fails Closed",
|
||||
"description": "Creation binds a task only after git succeeds. Removal accepts only a known path under the worktree root and refuses unverifiable or dirty state unless discard is explicit; it never completes the task as a side effect.",
|
||||
"alternatives": "Unconditional force removal is convenient, but can destroy unreviewed work and blur task completion with directory cleanup.",
|
||||
"zh": {
|
||||
"title": "Worktree 清理默认拒绝不安全操作",
|
||||
"description": "只有 git 创建成功后才绑定任务。删除仅接受 worktree 根目录下的已知路径;状态无法验证或存在未保存改动时,除非明确选择丢弃,否则拒绝删除,并且不会顺带完成任务。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "Worktree の片付けは安全側に失敗する",
|
||||
"description": "git の作成成功後にだけタスクを紐付ける。削除は worktree ルート配下の既知パスだけを受け付け、状態を確認できない場合や変更が残る場合は明示的な破棄なしに拒否し、タスクを副作用で完了させない。"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
"version": "s16",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "idle-state-discovers-work",
|
||||
"title": "Idle Teammates Look for Ready Work",
|
||||
"description": "s15 already keeps teammates alive in IDLE. s16 gives that state one more input: after waiting for messages, a teammate scans the shared task board for pending, unowned, unblocked work.",
|
||||
"alternatives": "The Lead could dispatch every assignment, but then an idle teammate cannot help with work that becomes ready later.",
|
||||
"id": "normalized-mcp-namespace",
|
||||
"title": "MCP Tools Use a Normalized Namespace",
|
||||
"description": "Discovered tools are exposed as mcp__server__tool. The prefix makes the source explicit and avoids collisions with built-in tools or tools from another server.",
|
||||
"alternatives": "Using the raw tool name is shorter, but search from two servers could overwrite each other.",
|
||||
"zh": {
|
||||
"title": "空闲队友主动寻找就绪任务",
|
||||
"description": "s15 已经让队友在 IDLE 中保持存活。s16 为这个状态增加任务板入口:等待消息后,队友会扫描 pending、未分配且依赖已完成的任务。"
|
||||
"title": "MCP 工具使用规范化命名空间",
|
||||
"description": "发现到的工具会暴露为 mcp__server__tool。前缀让工具来源明确,也避免和内置工具或其他服务器工具冲突。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "待機中のチームメイトが実行可能な仕事を探す",
|
||||
"description": "s15 ですでにチームメイトは IDLE のまま残る。s16 はその状態にタスクボード入口を追加し、メッセージ待機後に pending、未所有、依存解決済みのタスクを探す。"
|
||||
"title": "MCP ツールは正規化された名前空間を使う",
|
||||
"description": "発見されたツールは mcp__server__tool として公開されます。接頭辞により出所が明確になり、組み込みツールや別サーバーのツールとの衝突を避けます。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "atomic-claim",
|
||||
"title": "Claiming Is Atomic",
|
||||
"description": "The ownership check and task update run under one lock. When two teammates see the same ready task, only one can move it from pending to in_progress.",
|
||||
"alternatives": "Scanning and writing without a shared lock can assign the same task twice.",
|
||||
"id": "dynamic-tool-pool",
|
||||
"title": "Tool Discovery Updates the Active Tool Pool",
|
||||
"description": "After connecting to a server, the runtime assembles a new tool pool for the next LLM call. The model can only use MCP tools after discovery has made them visible.",
|
||||
"alternatives": "Preloading every possible MCP tool would create a huge prompt and expose capabilities the user did not request.",
|
||||
"zh": {
|
||||
"title": "任务认领必须原子化",
|
||||
"description": "所有权检查与任务更新在同一把锁内完成。两个队友同时看到一个就绪任务时,只有一个能把它从 pending 推进到 in_progress。"
|
||||
"title": "工具发现会更新活动工具池",
|
||||
"description": "连接服务器后,运行时会为下一次 LLM 调用组装新的工具池。模型只有在发现阶段让 MCP 工具可见之后,才能调用它们。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "タスク認領を原子的に行う",
|
||||
"description": "所有権確認とタスク更新を同じ lock の中で行う。二つのチームメイトが同じ実行可能タスクを見ても、pending から in_progress へ進められるのは一方だけである。"
|
||||
"title": "ツール発見がアクティブなツールプールを更新する",
|
||||
"description": "サーバー接続後、ランタイムは次の LLM 呼び出し用に新しいツールプールを組み立てます。MCP ツールは発見で可視化された後にのみモデルが利用できます。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dependencies-filter-readiness",
|
||||
"title": "Dependencies Define Readiness",
|
||||
"description": "The scan returns a task only when every blockedBy dependency is completed. A teammate with nothing ready remains idle instead of starting work out of order.",
|
||||
"alternatives": "Ignoring dependencies increases utilization, but produces work against unfinished inputs.",
|
||||
"id": "external-results-append-like-tools",
|
||||
"title": "External Results Reuse the Tool Result Path",
|
||||
"description": "MCP responses are appended to the conversation like ordinary tool results. This keeps the agent loop unchanged while still letting external systems participate.",
|
||||
"alternatives": "A separate external-response channel would make MCP feel special and require extra loop logic.",
|
||||
"zh": {
|
||||
"title": "依赖关系决定任务是否就绪",
|
||||
"description": "只有 blockedBy 中的依赖全部完成,扫描才会返回该任务。没有就绪任务的队友继续保持 IDLE,不会越过依赖提前开工。"
|
||||
"title": "外部结果复用 Tool Result 路径",
|
||||
"description": "MCP 响应会像普通 tool result 一样追加到对话中。这样 agent 循环无需改变,同时外部系统仍然可以参与。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "依存関係が実行可能性を決める",
|
||||
"description": "blockedBy の依存がすべて完了したタスクだけを走査結果に含める。実行可能な仕事がなければ IDLE を維持し、順序を飛ばして開始しない。"
|
||||
"title": "外部結果は tool result 経路を再利用する",
|
||||
"description": "MCP の応答は通常の tool result と同じように会話へ追加されます。エージェントループを変えずに外部システムを参加させられます。"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
"version": "s17",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "worktree-name-validation",
|
||||
"title": "Worktree Names Are Validated Before Git Runs",
|
||||
"description": "The tool validates names before creating branches or directories, so unsafe user input never reaches git or filesystem operations.",
|
||||
"alternatives": "Passing names directly to git is shorter, but it turns a collaboration feature into an injection hazard.",
|
||||
"id": "composition-over-new-loop",
|
||||
"title": "The Harness Composes Previous Layers",
|
||||
"description": "The integrated harness does not replace the loop with a new architecture. It composes memory, tasks, skills, background work, teams, worktrees, and MCP around the same core model-tool-result cycle.",
|
||||
"alternatives": "A new orchestration framework would look more impressive, but it would hide the continuity across the course.",
|
||||
"zh": {
|
||||
"title": "运行 Git 前先校验 Worktree 名称",
|
||||
"description": "工具在创建分支或目录前先校验名称,不让不安全的用户输入进入 git 或文件系统操作。"
|
||||
"title": "Harness 组合既有层,而不是换掉循环",
|
||||
"description": "集成后的 Harness 没有用新架构替换循环,而是把 memory、task、skill、后台任务、团队、worktree、MCP 组合到同一个模型-工具-结果循环周围。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "git 実行前に worktree 名を検証する",
|
||||
"description": "ブランチやディレクトリ作成前に名前を検証し、危険なユーザー入力が git やファイルシステム操作へ流れないようにします。"
|
||||
"title": "Harness は既存レイヤーを統合する",
|
||||
"description": "統合された Harness はループを新しい構造で置き換えません。memory、task、skill、バックグラウンド処理、チーム、worktree、MCP を同じ model-tool-result サイクルの周囲に合成します。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "task-bound-worktree",
|
||||
"title": "The Task Record Owns the Worktree Binding",
|
||||
"description": "A task stores its assigned worktree so future commands know where to run. The binding is explicit data, not a hidden convention based on naming or current working directory.",
|
||||
"alternatives": "Deriving the worktree path from branch names is convenient, but brittle when tasks are renamed or moved.",
|
||||
"id": "single-source-of-runtime-truth",
|
||||
"title": "Runtime State Has Named Sources",
|
||||
"description": "Context assembly pulls from named sources such as memory, task graph, skills, tool registry, and policy. This keeps a large agent debuggable because each piece of prompt context has an owner.",
|
||||
"alternatives": "Dumping everything into one prompt string is shorter, but it becomes impossible to tell which subsystem caused a bad decision.",
|
||||
"zh": {
|
||||
"title": "任务记录持有 Worktree 绑定关系",
|
||||
"description": "任务会记录自己分配到的 worktree,因此后续命令知道应该在哪里运行。这个绑定是显式数据,而不是依赖命名或当前目录的隐藏约定。"
|
||||
"title": "运行时状态来自具名来源",
|
||||
"description": "上下文组装从 memory、task graph、skills、tool registry、policy 等具名来源读取。大型 agent 因此仍可调试,因为每块 prompt context 都有清晰归属。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "タスクレコードが worktree の紐付けを持つ",
|
||||
"description": "タスクは割り当てられた worktree を保持し、後続コマンドは実行場所を把握できます。この紐付けは命名や現在ディレクトリに依存する暗黙の規約ではなく、明示的なデータです。"
|
||||
"title": "ランタイム状態には名前付きの出所がある",
|
||||
"description": "コンテキスト組み立ては memory、task graph、skills、tool registry、policy などの名前付きソースから取得します。各 prompt context に所有者があるため、大きなエージェントでもデバッグ可能です。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "lifecycle-event-stream",
|
||||
"title": "Lifecycle Events Stay Separate from Tool Results",
|
||||
"description": "Creation, status, keep, and removal events are emitted to a side-channel log. That makes worktree state observable without overloading the conversational transcript.",
|
||||
"alternatives": "Only returning tool results is simpler, but later debugging needs a durable audit trail of worktree lifecycle changes.",
|
||||
"id": "recovery-is-first-class",
|
||||
"title": "Recovery Is Part of the Main Flow",
|
||||
"description": "Compaction, error recovery, and asynchronous result collection are normal loop behavior. The harness handles recovery and resumption through named paths instead of scattered exception branches.",
|
||||
"alternatives": "Leaving recovery at the edges makes it harder to see which state is safe to resume.",
|
||||
"zh": {
|
||||
"title": "生命周期事件与工具结果分离",
|
||||
"description": "创建、状态、保留和移除事件会写入旁路日志。这样 worktree 状态可观察,同时不会把对话 transcript 塞满运行时事件。"
|
||||
"title": "恢复能力是一等流程",
|
||||
"description": "压缩、错误恢复和异步结果收集都属于正常循环。Harness 通过明确的路径处理恢复与续跑,而不是把逻辑散落在异常分支中。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "ライフサイクルイベントをツール結果から分離する",
|
||||
"description": "作成、状態、保持、削除のイベントはサイドチャネルログへ出力します。会話 transcript をランタイムイベントで埋めずに worktree 状態を観測できます。"
|
||||
"title": "リカバリは主要フローの一部",
|
||||
"description": "圧縮、エラー回復、非同期結果収集を通常のループ動作として扱います。Harness は回復と再開を名前付きの経路にまとめ、例外分岐へ散らしません。"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
"version": "s18",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "normalized-mcp-namespace",
|
||||
"title": "MCP Tools Use a Normalized Namespace",
|
||||
"description": "Discovered tools are exposed as mcp__server__tool. The prefix makes the source explicit and avoids collisions with built-in tools or tools from another server.",
|
||||
"alternatives": "Using the raw tool name is shorter, but search from two servers could overwrite each other.",
|
||||
"id": "script-owns-fixed-orchestration",
|
||||
"title": "Code Owns Fixed Orchestration",
|
||||
"description": "When the stages and aggregation rules are known in advance, a workflow script makes the process parallel, reproducible, and inspectable without changing the main agent loop.",
|
||||
"alternatives": "Letting the model choose every next step is more flexible, but slower and harder to resume for a fixed procedure.",
|
||||
"zh": {
|
||||
"title": "MCP 工具使用规范化命名空间",
|
||||
"description": "发现到的工具会暴露为 mcp__server__tool。前缀让工具来源明确,也避免和内置工具或其他服务器工具冲突。"
|
||||
"title": "固定编排由代码负责",
|
||||
"description": "当阶段与汇总规则事先确定时,workflow 脚本能让流程并行、可复现、可检查,同时不修改主 Agent 循环。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "MCP ツールは正規化された名前空間を使う",
|
||||
"description": "発見されたツールは mcp__server__tool として公開されます。接頭辞により出所が明確になり、組み込みツールや別サーバーのツールとの衝突を避けます。"
|
||||
"title": "固定された編成はコードが担う",
|
||||
"description": "段階と集約ルールが事前に決まっているなら、workflow script は main Agent loop を変えずに処理を並列化し、再現可能で検査可能にする。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dynamic-tool-pool",
|
||||
"title": "Tool Discovery Updates the Active Tool Pool",
|
||||
"description": "After connecting to a server, the runtime assembles a new tool pool for the next LLM call. The model can only use MCP tools after discovery has made them visible.",
|
||||
"alternatives": "Preloading every possible MCP tool would create a huge prompt and expose capabilities the user did not request.",
|
||||
"id": "semantic-journal-keys",
|
||||
"title": "Semantic Keys Make Resume Independent of Completion Order",
|
||||
"description": "Journal entries use stable call content rather than a shared completion counter. Concurrent calls can finish in any order and still map to the correct cached result.",
|
||||
"alternatives": "Indexing by completion order is simpler, but replays the wrong result as soon as concurrent timing changes.",
|
||||
"zh": {
|
||||
"title": "工具发现会更新活动工具池",
|
||||
"description": "连接服务器后,运行时会为下一次 LLM 调用组装新的工具池。模型只有在发现阶段让 MCP 工具可见之后,才能调用它们。"
|
||||
"title": "语义键让恢复不依赖完成顺序",
|
||||
"description": "Journal 用稳定的调用内容作为 key,而不是共享完成计数器。并发调用无论以什么顺序结束,都能命中正确缓存。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "ツール発見がアクティブなツールプールを更新する",
|
||||
"description": "サーバー接続後、ランタイムは次の LLM 呼び出し用に新しいツールプールを組み立てます。MCP ツールは発見で可視化された後にのみモデルが利用できます。"
|
||||
"title": "意味キーで完了順序に依存せず再開する",
|
||||
"description": "Journal は共有完了カウンタではなく安定した call 内容を key にする。並行 call の終了順が変わっても正しい cache result に対応できる。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "external-results-append-like-tools",
|
||||
"title": "External Results Reuse the Tool Result Path",
|
||||
"description": "MCP responses are appended to the conversation like ordinary tool results. This keeps the agent loop unchanged while still letting external systems participate.",
|
||||
"alternatives": "A separate external-response channel would make MCP feel special and require extra loop logic.",
|
||||
"id": "fail-the-workflow",
|
||||
"title": "Orchestration Failures Propagate",
|
||||
"description": "A failed stage, invalid structured result, corrupt journal, or exceeded run-wide limit fails the workflow instead of silently dropping an item and reporting success.",
|
||||
"alternatives": "Best-effort collection can be useful for optional work, but it must be explicit rather than the default.",
|
||||
"zh": {
|
||||
"title": "外部结果复用 Tool Result 路径",
|
||||
"description": "MCP 响应会像普通 tool result 一样追加到对话中。这样 agent 循环无需改变,同时外部系统仍然可以参与。"
|
||||
"title": "编排故障必须向上传播",
|
||||
"description": "阶段失败、结构化结果不合法、journal 损坏或超过全局限制时,workflow 直接失败,而不是静默丢项后仍报告成功。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "外部結果は tool result 経路を再利用する",
|
||||
"description": "MCP の応答は通常の tool result と同じように会話へ追加されます。エージェントループを変えずに外部システムを参加させられます。"
|
||||
"title": "編成の失敗は上位へ伝播させる",
|
||||
"description": "stage failure、無効な structured result、破損 journal、run-wide limit 超過は workflow を失敗させ、項目を黙って落として成功扱いしない。"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
"version": "s19",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "composition-over-new-loop",
|
||||
"title": "The Harness Composes Previous Layers",
|
||||
"description": "The integrated harness does not replace the loop with a new architecture. It composes memory, tasks, skills, background work, teams, worktrees, and MCP around the same core model-tool-result cycle.",
|
||||
"alternatives": "A new orchestration framework would look more impressive, but it would hide the continuity across the course.",
|
||||
"id": "host-owns-completion-gate",
|
||||
"title": "The Host Owns the Completion Gate",
|
||||
"description": "The working model may request to stop, but GoalController evaluates the active goal before AgentSession returns. The gate sits at the existing turn boundary.",
|
||||
"alternatives": "Asking the working model whether it is finished is simpler, but lets the same actor make and verify its own claim.",
|
||||
"zh": {
|
||||
"title": "Harness 组合既有层,而不是换掉循环",
|
||||
"description": "集成后的 Harness 没有用新架构替换循环,而是把 memory、task、skill、后台任务、团队、worktree、MCP 组合到同一个模型-工具-结果循环周围。"
|
||||
"title": "完成闸门由宿主持有",
|
||||
"description": "工作模型可以请求停止,但 GoalController 会在 AgentSession 返回前评估 active goal。这个闸门就在原有的轮次边界上。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "Harness は既存レイヤーを統合する",
|
||||
"description": "統合された Harness はループを新しい構造で置き換えません。memory、task、skill、バックグラウンド処理、チーム、worktree、MCP を同じ model-tool-result サイクルの周囲に合成します。"
|
||||
"title": "完了ゲートはホストが所有する",
|
||||
"description": "作業モデルは停止を要求できますが、GoalController は AgentSession が return する前に active goal を評価します。この gate は既存の turn 境界に置かれます。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "single-source-of-runtime-truth",
|
||||
"title": "Runtime State Has Named Sources",
|
||||
"description": "Context assembly pulls from named sources such as memory, task graph, skills, tool registry, and policy. This keeps a large agent debuggable because each piece of prompt context has an owner.",
|
||||
"alternatives": "Dumping everything into one prompt string is shorter, but it becomes impossible to tell which subsystem caused a bad decision.",
|
||||
"id": "conversation-is-evaluator-input",
|
||||
"title": "The Conversation Is the Evaluator's Input",
|
||||
"description": "The evaluator receives the active condition and the current conversation, including tool results reported there. It has no tools of its own and judges only what the conversation contains.",
|
||||
"alternatives": "Letting the evaluator rerun commands would turn a completion check into another worker and create a second execution path.",
|
||||
"zh": {
|
||||
"title": "运行时状态来自具名来源",
|
||||
"description": "上下文组装从 memory、task graph、skills、tool registry、policy 等具名来源读取。大型 agent 因此仍可调试,因为每块 prompt context 都有清晰归属。"
|
||||
"title": "对话记录就是判断器的输入",
|
||||
"description": "判断器接收 active condition 和当前对话,其中也包括已经写入的工具结果。它自己没有工具,只能根据对话中已有的内容判断。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "ランタイム状態には名前付きの出所がある",
|
||||
"description": "コンテキスト組み立ては memory、task graph、skills、tool registry、policy などの名前付きソースから取得します。各 prompt context に所有者があるため、大きなエージェントでもデバッグ可能です。"
|
||||
"title": "conversation が evaluator の入力になる",
|
||||
"description": "evaluator は active condition と現在の conversation を受け取り、そこに記録された tool result も読みます。自身では tool を使えず、conversation にある内容だけで判断します。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "recovery-is-first-class",
|
||||
"title": "Recovery Is Part of the Main Flow",
|
||||
"description": "Compaction, error recovery, and asynchronous result collection are normal loop behavior. The harness handles recovery and resumption through named paths instead of scattered exception branches.",
|
||||
"alternatives": "Leaving recovery at the edges makes it harder to see which state is safe to resume.",
|
||||
"id": "continuation-limits-preserve-goal",
|
||||
"title": "Limits Return Control Without Clearing the Goal",
|
||||
"description": "When a goal is unmet, the controller appends the evaluator's reason to messages[] and continues the same loop. The Stop-hook block cap or global turn limit returns control to the user while leaving the goal active.",
|
||||
"alternatives": "Continuing without any limit can hold one request forever; marking the goal complete or clearing it at the limit would lose unfinished work.",
|
||||
"zh": {
|
||||
"title": "恢复能力是一等流程",
|
||||
"description": "压缩、错误恢复和异步结果收集都属于正常循环。Harness 通过明确的路径处理恢复与续跑,而不是把逻辑散落在异常分支中。"
|
||||
"title": "达到限制时交还控制权,但保留目标",
|
||||
"description": "目标未满足时,controller 把判断理由追加到 messages[],并在同一个循环里继续。Stop hook 的连续阻止上限或全局轮次上限会把控制权交还用户,同时让目标保持 active。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "リカバリは主要フローの一部",
|
||||
"description": "圧縮、エラー回復、非同期結果収集を通常のループ動作として扱います。Harness は回復と再開を名前付きの経路にまとめ、例外分岐へ散らしません。"
|
||||
"title": "上限では control を返し、goal は維持する",
|
||||
"description": "goal が未達なら、controller は evaluator の理由を messages[] に追加し、同じ loop を続けます。Stop hook の連続 block 上限または global turn limit に達すると、goal を active のまま user に control を返します。"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"version": "s20",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "script-owns-fixed-orchestration",
|
||||
"title": "Code Owns Fixed Orchestration",
|
||||
"description": "When the stages and aggregation rules are known in advance, a workflow script makes the process parallel, reproducible, and inspectable without changing the main agent loop.",
|
||||
"alternatives": "Letting the model choose every next step is more flexible, but slower and harder to resume for a fixed procedure.",
|
||||
"zh": {
|
||||
"title": "固定编排由代码负责",
|
||||
"description": "当阶段与汇总规则事先确定时,workflow 脚本能让流程并行、可复现、可检查,同时不修改主 Agent 循环。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "固定された編成はコードが担う",
|
||||
"description": "段階と集約ルールが事前に決まっているなら、workflow script は main Agent loop を変えずに処理を並列化し、再現可能で検査可能にする。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "semantic-journal-keys",
|
||||
"title": "Semantic Keys Make Resume Independent of Completion Order",
|
||||
"description": "Journal entries use stable call content rather than a shared completion counter. Concurrent calls can finish in any order and still map to the correct cached result.",
|
||||
"alternatives": "Indexing by completion order is simpler, but replays the wrong result as soon as concurrent timing changes.",
|
||||
"zh": {
|
||||
"title": "语义键让恢复不依赖完成顺序",
|
||||
"description": "Journal 用稳定的调用内容作为 key,而不是共享完成计数器。并发调用无论以什么顺序结束,都能命中正确缓存。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "意味キーで完了順序に依存せず再開する",
|
||||
"description": "Journal は共有完了カウンタではなく安定した call 内容を key にする。並行 call の終了順が変わっても正しい cache result に対応できる。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fail-the-workflow",
|
||||
"title": "Orchestration Failures Propagate",
|
||||
"description": "A failed stage, invalid structured result, corrupt journal, or exceeded run-wide limit fails the workflow instead of silently dropping an item and reporting success.",
|
||||
"alternatives": "Best-effort collection can be useful for optional work, but it must be explicit rather than the default.",
|
||||
"zh": {
|
||||
"title": "编排故障必须向上传播",
|
||||
"description": "阶段失败、结构化结果不合法、journal 损坏或超过全局限制时,workflow 直接失败,而不是静默丢项后仍报告成功。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "編成の失敗は上位へ伝播させる",
|
||||
"description": "stage failure、無効な structured result、破損 journal、run-wide limit 超過は workflow を失敗させ、項目を黙って落として成功扱いしない。"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"version": "s21",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "host-owns-completion-gate",
|
||||
"title": "The Host Owns the Completion Gate",
|
||||
"description": "The working model may request to stop, but the harness evaluates the active goal before returning. Completion is a program decision at the turn boundary.",
|
||||
"alternatives": "Asking the working model whether it is finished is simpler, but lets the same actor make and verify its own claim.",
|
||||
"zh": {
|
||||
"title": "完成闸门由宿主持有",
|
||||
"description": "工作模型可以请求停止,但 harness 会在 return 前评估 active goal。是否完成是轮次边界上的程序决策。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "完了ゲートはホストが所有する",
|
||||
"description": "作業モデルは停止を要求できるが、harness は return 前に active goal を評価する。完了は turn 境界でのプログラム判断である。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-assigned-evidence-origins",
|
||||
"title": "Evidence Trust Comes from the Ingress Path",
|
||||
"description": "Ordinary submit calls cannot attach trusted labels. Only an allowlisted host-event channel can deliver task or monitor evidence, so user and model prose cannot certify itself.",
|
||||
"alternatives": "Trusting text content or caller-supplied labels makes the evidence boundary forgeable.",
|
||||
"zh": {
|
||||
"title": "证据信任来自入口路径",
|
||||
"description": "普通 submit 不能附加可信标签;只有白名单宿主事件通道能送入 task 或 monitor 证据,因此用户与模型文本不能自证完成。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "証拠の信頼は入力経路から得る",
|
||||
"description": "通常の submit は trusted label を付けられず、allowlist 済み host event channel だけが task や monitor evidence を届ける。ユーザーやモデルの文章は自己証明できない。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bounded-continuation",
|
||||
"title": "Every Automatic Continuation Needs a Budget",
|
||||
"description": "An unmet goal queues another turn only while budget remains. Exhaustion marks the goal blocked and releases the gate instead of creating an infinite loop.",
|
||||
"alternatives": "An unbounded goal is persistent, but an impossible condition can consume resources forever.",
|
||||
"zh": {
|
||||
"title": "每次自动续轮都必须有预算",
|
||||
"description": "目标未满足时只在预算剩余时继续;耗尽后将目标标记为 blocked 并释放闸门,避免无限循环。"
|
||||
},
|
||||
"ja": {
|
||||
"title": "自動継続には必ず予算を置く",
|
||||
"description": "goal 未達時は予算が残る間だけ次の turn を追加する。使い切れば blocked にして gate を解放し、無限 loop を防ぐ。"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -370,88 +370,38 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
|
||||
nodes: [
|
||||
{ id: "start", label: "User Requirement", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "lead", label: "Lead Proposes\nSmall Team", type: "process", x: COL_CENTER, y: 110 },
|
||||
{ id: "team_tool", label: "User Confirms?", type: "decision", x: COL_CENTER, y: 200 },
|
||||
{ id: "spawn", label: "Spawn Persistent\nTeammates", type: "subprocess", x: COL_LEFT, y: 300 },
|
||||
{ id: "send", label: "Assignment /\nTyped Request", type: "subprocess", x: COL_LEFT, y: 400 },
|
||||
{ id: "bus", label: "MessageBus\nJSONL Mailboxes", type: "process", x: COL_CENTER, y: 500 },
|
||||
{ id: "teammate", label: "Teammate\nWORK / IDLE", type: "process", x: COL_RIGHT, y: 400 },
|
||||
{ id: "tools", label: "Scoped Tools /\nPlan Gate", type: "subprocess", x: COL_RIGHT, y: 500 },
|
||||
{ id: "inbox", label: "Runtime Delivery", type: "process", x: COL_CENTER, y: 600 },
|
||||
{ id: "append", label: "Append Team Events", type: "process", x: COL_LEFT, y: 690 },
|
||||
{ id: "end", label: "Continue Alone", type: "end", x: COL_RIGHT, y: 300 },
|
||||
{ id: "confirm", label: "User Confirms?", type: "decision", x: COL_CENTER, y: 190 },
|
||||
{ id: "spawn", label: "Spawn Persistent\nTeammate", type: "subprocess", x: COL_LEFT, y: 280 },
|
||||
{ id: "bus", label: "MessageBus\nTyped Requests", type: "process", x: COL_CENTER, y: 370 },
|
||||
{ id: "teammate", label: "Teammate\nWORK / IDLE", type: "process", x: COL_RIGHT, y: 280 },
|
||||
{ id: "gate", label: "Plan Approved?", type: "decision", x: COL_RIGHT, y: 370 },
|
||||
{ id: "scan", label: "Scan Ready Tasks", type: "subprocess", x: COL_CENTER, y: 470 },
|
||||
{ id: "ready", label: "Ready Task?", type: "decision", x: COL_CENTER, y: 560 },
|
||||
{ id: "claim", label: "Atomic Claim\ntask_lock", type: "subprocess", x: COL_LEFT, y: 650 },
|
||||
{ id: "cwd", label: "Task Worktree\nTool cwd", type: "process", x: COL_RIGHT, y: 650 },
|
||||
{ id: "result", label: "Result + IDLE\nRuntime Delivery", type: "process", x: COL_CENTER, y: 740 },
|
||||
{ id: "end", label: "Continue Alone", type: "end", x: COL_RIGHT, y: 190 },
|
||||
],
|
||||
edges: [
|
||||
{ from: "start", to: "lead" },
|
||||
{ from: "lead", to: "team_tool" },
|
||||
{ from: "team_tool", to: "spawn", label: "yes" },
|
||||
{ from: "team_tool", to: "end", label: "no" },
|
||||
{ from: "spawn", to: "send" },
|
||||
{ from: "send", to: "bus" },
|
||||
{ from: "lead", to: "confirm" },
|
||||
{ from: "confirm", to: "spawn", label: "yes" },
|
||||
{ from: "confirm", to: "end", label: "no" },
|
||||
{ from: "spawn", to: "bus", label: "assignment" },
|
||||
{ from: "bus", to: "teammate" },
|
||||
{ from: "teammate", to: "tools" },
|
||||
{ from: "tools", to: "bus", label: "result / protocol reply" },
|
||||
{ from: "bus", to: "inbox", label: "wake Lead" },
|
||||
{ from: "inbox", to: "append" },
|
||||
{ from: "append", to: "lead" },
|
||||
{ from: "teammate", to: "gate" },
|
||||
{ from: "gate", to: "bus", label: "waiting" },
|
||||
{ from: "gate", to: "scan", label: "approved / idle" },
|
||||
{ from: "scan", to: "ready" },
|
||||
{ from: "ready", to: "bus", label: "no: wait" },
|
||||
{ from: "ready", to: "claim", label: "yes" },
|
||||
{ from: "claim", to: "cwd" },
|
||||
{ from: "cwd", to: "result" },
|
||||
{ from: "result", to: "bus", label: "reply + wake Lead" },
|
||||
{ from: "bus", to: "lead", label: "runtime delivery" },
|
||||
],
|
||||
},
|
||||
s16: {
|
||||
nodes: [
|
||||
{ id: "start", label: "Teammate IDLE", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "idle", label: "Wait for Messages", type: "process", x: COL_CENTER, y: 110 },
|
||||
{ id: "scan", label: "Scan Ready Tasks", type: "subprocess", x: COL_CENTER, y: 190 },
|
||||
{ id: "claimable", label: "Ready Task?", type: "decision", x: COL_CENTER, y: 280 },
|
||||
{ id: "claim", label: "Atomic Claim\ntask_lock", type: "subprocess", x: COL_LEFT, y: 380 },
|
||||
{ id: "work", label: "WORK State", type: "process", x: COL_LEFT, y: 470 },
|
||||
{ id: "complete", label: "complete_task", type: "subprocess", x: COL_LEFT, y: 560 },
|
||||
{ id: "inbox", label: "No Ready Task", type: "process", x: COL_RIGHT, y: 380 },
|
||||
{ id: "shutdown", label: "Remain IDLE", type: "process", x: COL_RIGHT, y: 470 },
|
||||
{ id: "done", label: "Result + IDLE Event", type: "process", x: COL_CENTER, y: 650 },
|
||||
],
|
||||
edges: [
|
||||
{ from: "start", to: "idle" },
|
||||
{ from: "idle", to: "scan" },
|
||||
{ from: "scan", to: "claimable" },
|
||||
{ from: "claimable", to: "claim", label: "yes" },
|
||||
{ from: "claimable", to: "inbox", label: "no" },
|
||||
{ from: "claim", to: "work" },
|
||||
{ from: "work", to: "complete" },
|
||||
{ from: "complete", to: "done" },
|
||||
{ from: "done", to: "idle" },
|
||||
{ from: "inbox", to: "shutdown" },
|
||||
{ from: "shutdown", to: "idle" },
|
||||
],
|
||||
},
|
||||
s17: {
|
||||
nodes: [
|
||||
{ id: "start", label: "Task Selected", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "create", label: "create_worktree", type: "subprocess", x: COL_CENTER, y: 110 },
|
||||
{ id: "validate", label: "Validate Name", type: "process", x: COL_CENTER, y: 190 },
|
||||
{ id: "git", label: "git worktree add", type: "subprocess", x: COL_LEFT, y: 290 },
|
||||
{ id: "bind", label: "Bind Task\nworktree field", type: "process", x: COL_LEFT, y: 380 },
|
||||
{ id: "run", label: "Run in Isolated\nDirectory", type: "subprocess", x: COL_CENTER, y: 470 },
|
||||
{ id: "events", label: "Lifecycle Events\n.events.jsonl", type: "process", x: COL_RIGHT, y: 190 },
|
||||
{ id: "close", label: "keep / remove", type: "decision", x: COL_CENTER, y: 560 },
|
||||
{ id: "cleanup", label: "remove_worktree", type: "subprocess", x: COL_LEFT, y: 650 },
|
||||
{ id: "keep", label: "keep_worktree", type: "process", x: COL_RIGHT, y: 650 },
|
||||
{ id: "end", label: "Task Result", type: "end", x: COL_CENTER, y: 740 },
|
||||
],
|
||||
edges: [
|
||||
{ from: "start", to: "create" },
|
||||
{ from: "create", to: "validate" },
|
||||
{ from: "validate", to: "git" },
|
||||
{ from: "git", to: "bind" },
|
||||
{ from: "bind", to: "run" },
|
||||
{ from: "create", to: "events", label: "emit" },
|
||||
{ from: "run", to: "events", label: "status" },
|
||||
{ from: "run", to: "close" },
|
||||
{ from: "close", to: "cleanup", label: "remove" },
|
||||
{ from: "close", to: "keep", label: "keep" },
|
||||
{ from: "cleanup", to: "end" },
|
||||
{ from: "keep", to: "end" },
|
||||
],
|
||||
},
|
||||
s18: {
|
||||
nodes: [
|
||||
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 110 },
|
||||
@@ -478,7 +428,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
|
||||
{ from: "append", to: "llm" },
|
||||
],
|
||||
},
|
||||
s19: {
|
||||
s17: {
|
||||
nodes: [
|
||||
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "context", label: "Assemble Context\nmemory + tasks", type: "process", x: COL_CENTER, y: 115 },
|
||||
@@ -512,7 +462,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
|
||||
{ from: "recover", to: "context" },
|
||||
],
|
||||
},
|
||||
s20: {
|
||||
s18: {
|
||||
nodes: [
|
||||
{ id: "start", label: "Workflow Tool Call", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "validate", label: "Validate Meta +\nPermission", type: "process", x: COL_CENTER, y: 120 },
|
||||
@@ -536,28 +486,34 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
|
||||
{ from: "output", to: "notify" },
|
||||
],
|
||||
},
|
||||
s21: {
|
||||
s19: {
|
||||
nodes: [
|
||||
{ id: "start", label: "Model Wants to Stop", type: "start", x: COL_CENTER, y: 30 },
|
||||
{ id: "active", label: "Active Goal?", type: "decision", x: COL_CENTER, y: 120 },
|
||||
{ id: "evidence", label: "Read Trusted\nEvidence Window", type: "process", x: COL_CENTER, y: 220 },
|
||||
{ id: "evaluate", label: "Goal Satisfied?", type: "decision", x: COL_CENTER, y: 320 },
|
||||
{ id: "complete", label: "goal_completed", type: "end", x: COL_RIGHT, y: 430 },
|
||||
{ id: "budget", label: "Budget Left?", type: "decision", x: COL_LEFT, y: 430 },
|
||||
{ id: "continue", label: "Queue Goal\nContinuation", type: "process", x: COL_LEFT, y: 540 },
|
||||
{ id: "loop", label: "Next Agent Turn", type: "subprocess", x: COL_LEFT, y: 650 },
|
||||
{ id: "blocked", label: "goal_blocked", type: "end", x: COL_CENTER, y: 540 },
|
||||
{ id: "background", label: "Background Work\nRunning?", type: "decision", x: COL_CENTER, y: 215 },
|
||||
{ id: "defer", label: "defer\nGoal Stays Active", type: "end", x: COL_RIGHT, y: 215 },
|
||||
{ id: "conversation", label: "Evaluator Reads\nConversation", type: "process", x: COL_CENTER, y: 315 },
|
||||
{ id: "evaluate", label: "Evaluator Result?", type: "decision", x: COL_CENTER, y: 415 },
|
||||
{ id: "complete", label: "achieved\nGoal Cleared", type: "end", x: COL_RIGHT, y: 415 },
|
||||
{ id: "failed", label: "failed\nGoal Cleared", type: "end", x: COL_RIGHT, y: 520 },
|
||||
{ id: "cap", label: "Stop-Block Cap\nReached?", type: "decision", x: COL_LEFT, y: 520 },
|
||||
{ id: "continue", label: "Append Reason\nto messages[]", type: "process", x: COL_LEFT, y: 625 },
|
||||
{ id: "loop", label: "Next Agent Turn", type: "subprocess", x: COL_LEFT, y: 730 },
|
||||
{ id: "limit", label: "limit\nGoal Stays Active", type: "end", x: COL_CENTER, y: 520 },
|
||||
{ id: "return", label: "Return", type: "end", x: COL_RIGHT, y: 120 },
|
||||
],
|
||||
edges: [
|
||||
{ from: "start", to: "active" },
|
||||
{ from: "active", to: "return", label: "no" },
|
||||
{ from: "active", to: "evidence", label: "yes" },
|
||||
{ from: "evidence", to: "evaluate" },
|
||||
{ from: "evaluate", to: "complete", label: "yes" },
|
||||
{ from: "evaluate", to: "budget", label: "no" },
|
||||
{ from: "budget", to: "continue", label: "yes" },
|
||||
{ from: "budget", to: "blocked", label: "no" },
|
||||
{ from: "active", to: "background", label: "yes" },
|
||||
{ from: "background", to: "defer", label: "yes" },
|
||||
{ from: "background", to: "conversation", label: "no" },
|
||||
{ from: "conversation", to: "evaluate" },
|
||||
{ from: "evaluate", to: "complete", label: "ok" },
|
||||
{ from: "evaluate", to: "failed", label: "impossible" },
|
||||
{ from: "evaluate", to: "cap", label: "not yet" },
|
||||
{ from: "cap", to: "limit", label: "yes" },
|
||||
{ from: "cap", to: "continue", label: "no" },
|
||||
{ from: "continue", to: "loop" },
|
||||
{ from: "loop", to: "start" },
|
||||
],
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,60 +1,105 @@
|
||||
{
|
||||
"version": "s15",
|
||||
"title": "Agent Teams",
|
||||
"description": "The Lead proposes a team for a normal user request, waits for confirmation, then coordinates persistent teammates through runtime-delivered events.",
|
||||
"title": "Agent Team Runtime",
|
||||
"description": "The Lead coordinates persistent teammates while the runtime owns message delivery, atomic task claims, and task-bound worktree directories.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Refactor the configuration loader and authentication flow, then update the tests.",
|
||||
"annotation": "The user describes the goal without designing a team."
|
||||
"content": "Refactor authentication and update its dependent tests in parallel without mixing the edits.",
|
||||
"annotation": "The user states the outcome and parallel-edit boundary, not an implementation-specific worktree procedure."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "These areas can be handled in parallel. I propose one teammate for configuration and authentication, and another for tests. Shall I start that team?",
|
||||
"annotation": "The Lead chooses a small team and makes the extra-agent decision visible."
|
||||
"content": "I propose a backend teammate for authentication and a test teammate that picks up the dependent checks once they are ready. Shall I start the team?",
|
||||
"annotation": "The Lead makes the extra-agent decision visible before spawning."
|
||||
},
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Yes, go ahead.",
|
||||
"annotation": "Teammates start only after explicit confirmation."
|
||||
"annotation": "The team starts only after explicit confirmation."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_task",
|
||||
"content": "{\"subject\":\"Refactor authentication\"}",
|
||||
"annotation": "The shared task board remains the source of ownership and status."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "create_task",
|
||||
"content": "Created task_1712345678_0042: Refactor authentication",
|
||||
"annotation": "The runtime-generated task ID is carried into every later operation on this task."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_worktree",
|
||||
"content": "{\"name\":\"auth-refactor\",\"task_id\":\"task_1712345678_0042\"}",
|
||||
"annotation": "The worktree directory is recorded on the task instead of managed as a separate workflow or security sandbox."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_task",
|
||||
"content": "{\"subject\":\"Update authentication tests\",\"blockedBy\":[\"task_1712345678_0042\"]}",
|
||||
"annotation": "The task graph keeps dependent work from starting early."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "create_task",
|
||||
"content": "Created task_1712345678_0043: Update authentication tests (blockedBy: task_1712345678_0042)",
|
||||
"annotation": "The second generated ID names the dependent task that the test teammate will later claim."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "spawn_teammate",
|
||||
"content": "{\"name\":\"backend\",\"role\":\"backend engineer\",\"prompt\":\"Refactor configuration and authentication while preserving interfaces.\"}",
|
||||
"annotation": "The first persistent teammate enters WORK with a focused assignment."
|
||||
"content": "{\"name\":\"backend\",\"role\":\"backend engineer\",\"prompt\":\"Claim the authentication task and propose a plan.\"}",
|
||||
"annotation": "A persistent teammate receives focused work through the team runtime."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "spawn_teammate",
|
||||
"content": "{\"name\":\"tests\",\"role\":\"test engineer\",\"prompt\":\"Update and run tests for the refactor.\"}",
|
||||
"annotation": "The second teammate gets an independent slice."
|
||||
"content": "{\"name\":\"tests\",\"role\":\"test engineer\",\"prompt\":\"Watch the board and claim the dependent test task when it becomes ready.\"}",
|
||||
"annotation": "A second persistent teammate can wait in IDLE without another direct dispatch."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "backend -> Lead: result(refactor complete) -> idle_notification",
|
||||
"content": "plan_request(req_plan_7) -> plan_response(req_plan_7, approved=true)",
|
||||
"annotation": "Typed correlation and an approval gate protect mutating tools."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "claim_next_task(backend) -> task_1712345678_0042; task_lock commits owner=backend",
|
||||
"annotation": "The ownership check and state transition are atomic."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "backend tool cwd -> .worktrees/auth-refactor",
|
||||
"annotation": "Bash, read, and write derive their directory from the claimed task binding."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "complete_task",
|
||||
"content": "{\"task_id\":\"task_1712345678_0042\"}",
|
||||
"annotation": "Completing the first task makes its dependent test task ready."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "backend -> Lead: result(auth refactor complete) -> idle_notification",
|
||||
"annotation": "Result and idle state are separate events; the teammate remains available."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "tests -> Lead: result(test suite passed) -> idle_notification",
|
||||
"annotation": "The runtime observes mailbox writes and wakes the Lead without a polling tool."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "Both parts are complete. The interfaces were preserved and the updated tests pass.",
|
||||
"annotation": "The Lead combines teammate results into one user-facing answer."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "request_shutdown",
|
||||
"content": "{\"teammate\":\"backend\"}",
|
||||
"annotation": "A typed request with a request id closes the persistent teammate cleanly."
|
||||
"content": "claim_next_task(tests) -> task_1712345678_0043; task_lock commits owner=tests",
|
||||
"annotation": "An idle teammate discovers newly ready work without another direct assignment."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "shutdown_response(request_id=req_42, approve=true)",
|
||||
"annotation": "The matching response resolves the pending protocol request."
|
||||
"content": "tests -> Lead: result(test suite passed) -> idle_notification",
|
||||
"annotation": "The runtime wakes the Lead when mailbox events arrive instead of asking the model to poll."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "Authentication was refactored in its task-bound worktree and the dependent tests pass.",
|
||||
"annotation": "The Lead combines parallel results into one user-facing outcome."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
{
|
||||
"version": "s16",
|
||||
"title": "Autonomous Agents",
|
||||
"description": "Idle teammates discover ready tasks on a shared board and use an atomic claim before starting work.",
|
||||
"title": "MCP Tools",
|
||||
"description": "The agent discovers external MCP tools and exposes them through a normalized tool namespace.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Update the API examples, then add tests that use the final examples.",
|
||||
"annotation": "The request contains two tasks with a clear dependency."
|
||||
"content": "Search the documentation for deployment guidance.",
|
||||
"annotation": "The user asks for a tool source outside the built-in set."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_task",
|
||||
"content": "{\"subject\":\"Update API examples\"}",
|
||||
"annotation": "The Lead creates the first task in the shared graph."
|
||||
"toolName": "connect_mcp",
|
||||
"content": "{\"name\":\"docs\"}",
|
||||
"annotation": "The runtime creates an MCP client for the named server."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "normalize_mcp_name(\"docs\", \"search\") -> mcp__docs__search",
|
||||
"annotation": "External tools are namespaced to avoid collisions."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "connect_mcp",
|
||||
"content": "Connected to MCP server 'docs'. Discovered 2 tools: search, get_version",
|
||||
"annotation": "Tool discovery expands the active tool pool."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_task",
|
||||
"content": "{\"subject\":\"Add example tests\",\"blockedBy\":[\"task_examples\"]}",
|
||||
"annotation": "The second task cannot start until the examples are complete."
|
||||
"toolName": "mcp__docs__search",
|
||||
"content": "{\"query\":\"deployment\"}",
|
||||
"annotation": "The LLM can now call the discovered tool by its normalized name."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "alice, bob: state=IDLE -> scan_unclaimed_tasks()",
|
||||
"annotation": "Existing IDLE teammates scan the board after waiting for messages."
|
||||
"type": "tool_result",
|
||||
"toolName": "mcp__docs__search",
|
||||
"content": "[docs] Found 3 results for 'deployment'",
|
||||
"annotation": "The external result is appended like any other tool result."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "claim_next_task(alice) -> task_examples; task_lock commits owner=alice",
|
||||
"annotation": "The ownership check and pending-to-in_progress update are atomic."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "claim_next_task(bob) -> no ready task; remain IDLE",
|
||||
"annotation": "The test task is still blocked, so Bob does not start it early."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "complete_task",
|
||||
"content": "{\"task_id\":\"task_examples\"}",
|
||||
"annotation": "Completing the examples unblocks the dependent test task."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "claim_next_task(bob) -> task_tests; task_lock commits owner=bob",
|
||||
"annotation": "Bob claims the newly ready work without another direct assignment."
|
||||
"type": "assistant_text",
|
||||
"content": "The docs server found three matches for deployment guidance.",
|
||||
"annotation": "The agent summarizes external tool output for the user."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,45 +1,97 @@
|
||||
{
|
||||
"version": "s17",
|
||||
"title": "Worktree Isolation",
|
||||
"description": "A task can be bound to an isolated git worktree so concurrent agents avoid stepping on each other.",
|
||||
"title": "Integrated Harness",
|
||||
"description": "The harness composes context assembly, tools, memory, teams, background work, cron, worktrees, and MCP.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Update the docs and parser in parallel without letting the changes interfere.",
|
||||
"annotation": "Concurrent edits need isolated working directories."
|
||||
"content": "Prepare this project for release. Check the code, update the docs, and report deployment readiness.",
|
||||
"annotation": "One ordinary request needs several earlier capabilities to work together."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "assemble_prompt: memory + task graph + skills + available tools + policy",
|
||||
"annotation": "The runtime builds the prompt from layered sources of context."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "I suggest a docs teammate while I run the release checks and inspect deployment status. Shall I start that team?",
|
||||
"annotation": "The Lead proposes the team before adding another persistent agent."
|
||||
},
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Yes. Start the docs teammate and continue.",
|
||||
"annotation": "User confirmation opens the team boundary inherited from S15."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "load_skill",
|
||||
"content": "{\"name\":\"code-review\"}",
|
||||
"annotation": "Skills contribute procedural context before execution."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_task",
|
||||
"content": "{\"subject\":\"Update release notes\",\"description\":\"Prepare release documentation and report the result.\"}",
|
||||
"annotation": "The shared task board gives the docs work a stable owner and lifecycle."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "create_task",
|
||||
"content": "Created task_1712345678_0042: Update release notes",
|
||||
"annotation": "The task ID connects the assignment to its working directory."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "create_worktree",
|
||||
"content": "{\"task_id\":\"task_docs\",\"name\":\"docs-fix\"}",
|
||||
"annotation": "The tool validates a safe worktree name before touching git."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "git worktree add .worktrees/docs-fix -b agent/docs-fix",
|
||||
"annotation": "A separate branch and checkout are created for that task."
|
||||
"content": "{\"name\":\"release-docs\",\"task_id\":\"task_1712345678_0042\"}",
|
||||
"annotation": "The pending docs task receives a separate checkout before it is claimed."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "create_worktree",
|
||||
"content": "task_docs bound to .worktrees/docs-fix",
|
||||
"annotation": "The task record stores the assigned worktree path."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": ".worktrees/events.jsonl <- {event:\"created\", task:\"task_docs\", worktree:\"docs-fix\"}",
|
||||
"annotation": "Lifecycle events are emitted as a side channel."
|
||||
"content": "Worktree 'release-docs' created for task_1712345678_0042",
|
||||
"annotation": "The task now carries the checkout used by its eventual owner."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "keep_worktree",
|
||||
"content": "{\"task_id\":\"task_docs\",\"reason\":\"needs human review\"}",
|
||||
"annotation": "Closeout can preserve a worktree instead of deleting it immediately."
|
||||
"toolName": "spawn_teammate",
|
||||
"content": "{\"name\":\"docs\",\"role\":\"documentation engineer\",\"prompt\":\"Claim task_1712345678_0042, update the release notes in its task-bound worktree, and report the result.\"}",
|
||||
"annotation": "Team delegation handles a parallel slice of the release."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "bash",
|
||||
"content": "{\"command\":\"npm run lint && npm run test\",\"run_in_background\":true}",
|
||||
"annotation": "Long-running validation goes through the background task path."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "connect_mcp",
|
||||
"content": "{\"name\":\"deploy\"}",
|
||||
"annotation": "External capabilities are added only when needed."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "mcp__deploy__status",
|
||||
"content": "{\"service\":\"web\"}",
|
||||
"annotation": "Connecting discovers the tool; a separate call performs the status check."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "mcp__deploy__status",
|
||||
"content": "[deploy] web: running (v1.4.2)",
|
||||
"annotation": "The readiness statement now comes from the MCP tool result rather than the connection event."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "recover: background task done, teammate replied, deploy status result appended",
|
||||
"annotation": "The integrated runtime folds asynchronous results back into the loop."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "The docs task now has an isolated worktree and can be reviewed independently from parser changes.",
|
||||
"annotation": "The user sees the isolation boundary, not just a raw git command."
|
||||
"content": "Release preparation is ready: checks passed, docs are updated in the teammate worktree, and deployment status is available.",
|
||||
"annotation": "The integrated harness returns a single user-facing outcome."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,46 +1,54 @@
|
||||
{
|
||||
"version": "s18",
|
||||
"title": "MCP Tools",
|
||||
"description": "The agent discovers external MCP tools and exposes them through a normalized tool namespace.",
|
||||
"title": "Workflow Runtime",
|
||||
"description": "A fixed review pipeline executes inside one Workflow call, records a journal, emits lifecycle and progress events, and returns one final tool result.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Search the documentation for deployment guidance.",
|
||||
"annotation": "The user asks for a tool source outside the built-in set."
|
||||
"content": "Review this change across correctness, security, performance, and style.",
|
||||
"annotation": "The goal is open-ended, but the review procedure has a fixed shape."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "connect_mcp",
|
||||
"content": "{\"server\":\"docs\",\"command\":\"mock-docs-server\"}",
|
||||
"annotation": "The runtime creates an MCP client for the named server."
|
||||
"toolName": "Workflow",
|
||||
"content": "{\"name\":\"review-changes\",\"description\":\"Review changed files across dimensions and verify each finding\",\"phases\":[\"Review\",\"Verify\"]}",
|
||||
"annotation": "One tool call hands deterministic orchestration to the workflow runtime."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "normalize_mcp_name(\"docs\", \"search\") -> mcp__docs__search",
|
||||
"annotation": "External tools are namespaced to avoid collisions."
|
||||
"content": "async_launched(runId=wf_review-changes_6779) -> task_started",
|
||||
"annotation": "The runtime emits launch lifecycle events before it executes the script; this is not a tool result."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "phase=Review -> pipeline([correctness, security, performance, style])",
|
||||
"annotation": "Each item advances independently through the scripted stages."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "phase=Verify -> adversarial checks run in parallel",
|
||||
"annotation": "Structured results cross a validation boundary before aggregation."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "workflow_agent status=done -> journal append",
|
||||
"annotation": "Every completed agent call is checkpointed as the script runs."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "task_notification(status=completed, outputFile=.runtime/wf_review-changes_6779.output.json)",
|
||||
"annotation": "The task emits its final lifecycle event after output is written."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "connect_mcp",
|
||||
"content": "Connected docs with tools: mcp__docs__search, mcp__docs__read",
|
||||
"annotation": "Tool discovery expands the active tool pool."
|
||||
"toolName": "Workflow",
|
||||
"content": "{\"launched\":{\"status\":\"async_launched\",\"runId\":\"wf_review-changes_6779\"},\"result\":{\"confirmed\":[]},\"task\":{\"status\":\"completed\"}}",
|
||||
"annotation": "The completed call returns once, with launch metadata, the workflow result, and task state together."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "mcp__docs__search",
|
||||
"content": "{\"query\":\"deployment\"}",
|
||||
"annotation": "The LLM can now call the discovered tool by its normalized name."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"toolName": "mcp__docs__search",
|
||||
"content": "[\"Deploy with npm run build\", \"Use environment variables for tokens\"]",
|
||||
"annotation": "The external result is appended like any other tool result."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "The docs server says to build first and keep tokens in environment variables.",
|
||||
"annotation": "The agent summarizes external tool output for the user."
|
||||
"type": "system_event",
|
||||
"content": "append Workflow tool_result -> messages[]",
|
||||
"annotation": "The main loop receives that single result and continues with the updated conversation."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,57 +1,49 @@
|
||||
{
|
||||
"version": "s19",
|
||||
"title": "Integrated Harness",
|
||||
"description": "The harness composes context assembly, tools, memory, teams, background work, cron, worktrees, and MCP.",
|
||||
"title": "Goal Loop",
|
||||
"description": "A separate evaluator reads the conversation at the end of a turn and sends unfinished work back through the same loop.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Prepare this project for release. Check the code, update the docs, and report deployment readiness.",
|
||||
"annotation": "One ordinary request needs several earlier capabilities to work together."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "assemble_prompt: memory + task graph + skills + available tools + policy",
|
||||
"annotation": "The runtime builds the prompt from layered sources of context."
|
||||
"content": "/goal pytest tests/auth exits with code 0",
|
||||
"annotation": "The command stores an active completion condition and starts the work."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "I'll inspect the task graph, delegate docs, start the slow scan in the background, and use the release checklist skill.",
|
||||
"annotation": "The model chooses multiple mechanisms while keeping one coherent plan."
|
||||
"content": "The authentication code looks correct. The tests should pass now.",
|
||||
"annotation": "With no more tool calls, the session asks the evaluator whether the active goal is complete."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "load_skill",
|
||||
"content": "{\"name\":\"release-check\"}",
|
||||
"annotation": "Skills contribute procedural context before execution."
|
||||
"type": "system_event",
|
||||
"content": "goal_evaluated ok=false -> block",
|
||||
"annotation": "The evaluator finds no test exit code in the conversation."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "spawn_teammate",
|
||||
"content": "{\"name\":\"docs\",\"role\":\"Update release notes in its worktree.\"}",
|
||||
"annotation": "Team delegation handles a parallel slice of the release."
|
||||
"type": "system_event",
|
||||
"content": "[Goal still active] Run pytest tests/auth and surface the exit code.",
|
||||
"annotation": "The controller appends the reason to messages[] and continues the same loop; no queue is involved."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "bash",
|
||||
"content": "npm run lint && npm run test",
|
||||
"toolInput": "{\"background\":true}",
|
||||
"annotation": "Long-running validation goes through the background task path."
|
||||
"content": "pytest tests/auth",
|
||||
"annotation": "The next agent turn runs the missing check."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "connect_mcp",
|
||||
"content": "{\"server\":\"deploy\",\"command\":\"mock-deploy-server\"}",
|
||||
"annotation": "External capabilities are added only when needed."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "recover: background task done, teammate replied, MCP deploy status ready",
|
||||
"annotation": "The integrated runtime folds asynchronous results back into the loop."
|
||||
"type": "tool_result",
|
||||
"toolName": "bash",
|
||||
"content": "exit_code=0\n12 passed in 0.42s",
|
||||
"annotation": "The concrete command result enters the same conversation."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "Release preparation is ready: checks passed, docs are updated in the teammate worktree, and deployment status is available.",
|
||||
"annotation": "The integrated harness returns a single user-facing outcome."
|
||||
"content": "pytest tests/auth exited 0: 12 tests passed.",
|
||||
"annotation": "A turn with no further tool call reaches the Stop hook again."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "goal_evaluated ok=true -> achieved",
|
||||
"annotation": "The controller records success, clears the active goal, and returns."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"version": "s20",
|
||||
"title": "Workflow Runtime",
|
||||
"description": "A fixed review pipeline runs in parallel, records a journal, and resumes completed agent calls from cache.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "Review this change across correctness, security, performance, and style.",
|
||||
"annotation": "The goal is open-ended, but the review procedure has a fixed shape."
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
"toolName": "Workflow",
|
||||
"content": "{\"name\":\"review-changes\",\"phases\":[\"Review\",\"Verify\"]}",
|
||||
"annotation": "One tool call hands deterministic orchestration to the workflow runtime."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": "{\"status\":\"async_launched\",\"runId\":\"wf_review-changes_6779\"}",
|
||||
"annotation": "The product runtime returns a claim ticket while work continues in the background."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "phase=Review -> pipeline([correctness, security, performance, style])",
|
||||
"annotation": "Each item advances independently through the scripted stages."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "phase=Verify -> adversarial checks run in parallel",
|
||||
"annotation": "Structured results cross a validation boundary before aggregation."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "journal append -> task_notification(status=completed)",
|
||||
"annotation": "Every completed agent call is checkpointed before the final notification."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "resumeFromRunId -> unchanged calls status=cached",
|
||||
"annotation": "Semantic keys reuse completed work without depending on concurrency order."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"version": "s21",
|
||||
"title": "Goal Loop",
|
||||
"description": "A host-owned completion gate keeps the turn alive until trusted evidence satisfies the active goal.",
|
||||
"steps": [
|
||||
{
|
||||
"type": "user_message",
|
||||
"content": "/goal until tests passed and deploy green",
|
||||
"annotation": "The command defines the condition but sits outside the evidence window."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "goal_started -> evidence window reset",
|
||||
"annotation": "The harness, not the working model, owns the completion gate."
|
||||
},
|
||||
{
|
||||
"type": "assistant_text",
|
||||
"content": "Tests passed and deployment is green.",
|
||||
"annotation": "Assistant prose is not trusted completion evidence."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "goal_evaluated satisfied=false -> continuation queued",
|
||||
"annotation": "An unmet condition pushes the loop into another bounded turn."
|
||||
},
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": "<task_notification>tests passed; deploy green</task_notification>",
|
||||
"annotation": "Only an allowlisted host event can attach a trusted evidence origin."
|
||||
},
|
||||
{
|
||||
"type": "system_event",
|
||||
"content": "goal_evaluated satisfied=true -> goal_completed",
|
||||
"annotation": "Trusted evidence closes the goal and releases the stop gate."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"meta": { "title": "Learn Claude Code", "description": "Build a nano Claude Code-like agent from 0 to 1, one mechanism at a time" },
|
||||
"nav": { "home": "Home", "timeline": "Timeline", "compare": "Compare", "layers": "Layers", "github": "GitHub" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "Build a nano Claude Code-like agent from 0 to 1, one mechanism at a time", "start": "Start Learning", "core_pattern": "The Core Pattern", "core_pattern_desc": "Every AI coding agent shares the same loop: call the model, execute tools, feed results back. The harness adds policy, permissions, memory, coordination, and lifecycle control around it.", "learning_path": "Learning Path", "learning_path_desc": "21 progressive sessions, from a simple loop to deterministic orchestration and goal closure", "layers_title": "Architectural Layers", "layers_desc": "Five orthogonal concerns that compose into a complete agent", "loc": "LOC", "learn_more": "Learn More", "versions_in_layer": "versions", "message_flow": "Message Growth", "message_flow_desc": "Watch the messages array grow as the agent loop executes" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "Build a nano Claude Code-like agent from 0 to 1, one mechanism at a time", "start": "Start Learning", "core_pattern": "The Core Pattern", "core_pattern_desc": "Every AI coding agent shares the same loop: call the model, execute tools, feed results back. The harness adds policy, permissions, memory, coordination, and lifecycle control around it.", "learning_path": "Learning Path", "learning_path_desc": "19 progressive sessions, from a simple loop to deterministic orchestration and goal closure", "layers_title": "Architectural Layers", "layers_desc": "Five orthogonal concerns that compose into a complete agent", "loc": "LOC", "learn_more": "Learn More", "versions_in_layer": "versions", "message_flow": "Message Growth", "message_flow_desc": "Watch the messages array grow as the agent loop executes" },
|
||||
"version": { "loc": "lines of code", "tools": "tools", "new": "New", "prev": "Previous", "next": "Next", "view_source": "View Source", "view_diff": "View Diff", "design_decisions": "Design Decisions", "whats_new": "What's New", "tutorial": "Tutorial", "simulator": "Agent Loop Simulator", "execution_flow": "Execution Flow", "architecture": "Architecture", "concept_viz": "Concept Visualization", "alternatives": "Alternatives Considered", "tab_learn": "Learn", "tab_simulate": "Simulate", "tab_code": "Code", "tab_deep_dive": "Deep Dive" },
|
||||
"sim": { "play": "Play", "pause": "Pause", "step": "Step", "reset": "Reset", "speed": "Speed", "step_of": "of" },
|
||||
"timeline": { "title": "Learning Path", "subtitle": "s01 to s21: Progressive Agent Harness Design", "layer_legend": "Layer Legend", "loc_growth": "LOC Growth", "learn_more": "Learn More" },
|
||||
"timeline": { "title": "Learning Path", "subtitle": "s01 to s19: Progressive Agent Harness Design", "layer_legend": "Layer Legend", "loc_growth": "LOC Growth", "learn_more": "Learn More" },
|
||||
"layers": {
|
||||
"title": "Architectural Layers",
|
||||
"subtitle": "Five orthogonal concerns that compose into a complete agent",
|
||||
@@ -12,7 +12,7 @@
|
||||
"planning": "How work is organized. From simple todo lists to dependency-aware task boards shared across agents.",
|
||||
"memory": "Keeping context within limits. Compression strategies that let agents work infinitely without losing coherence.",
|
||||
"concurrency": "Non-blocking execution. Background threads and notification buses for parallel work.",
|
||||
"collaboration": "Multi-agent coordination. Teams, messaging, and autonomous teammates that think for themselves."
|
||||
"collaboration": "Multi-agent coordination. Teams, messaging, atomic task claims, and task-bound worktree directories."
|
||||
},
|
||||
"compare": {
|
||||
"title": "Compare Versions",
|
||||
@@ -53,13 +53,11 @@
|
||||
"s12": "Task System",
|
||||
"s13": "Background Tasks",
|
||||
"s14": "Cron Scheduler",
|
||||
"s15": "Agent Teams",
|
||||
"s16": "Autonomous Agents",
|
||||
"s17": "Worktree Isolation",
|
||||
"s18": "MCP Tools",
|
||||
"s19": "Integrated Harness",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "Goal Loop"
|
||||
"s15": "Agent Team Runtime",
|
||||
"s16": "MCP Tools",
|
||||
"s17": "Integrated Harness",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "Goal Loop"
|
||||
},
|
||||
"layer_labels": {
|
||||
"tools": "Tools & Execution",
|
||||
@@ -83,12 +81,10 @@
|
||||
"s12": "Task Board Dependencies",
|
||||
"s13": "Background Task Lanes",
|
||||
"s14": "Cron Scheduler",
|
||||
"s15": "Agent Teams and Protocols",
|
||||
"s16": "Autonomous Agent Cycle",
|
||||
"s17": "Worktree Task Isolation",
|
||||
"s18": "MCP Tool Bridge",
|
||||
"s19": "Integrated Harness Turn",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "Goal Completion Gate"
|
||||
"s15": "Team Runtime: Message, Claim, Bind",
|
||||
"s16": "MCP Tool Bridge",
|
||||
"s17": "Integrated Harness Turn",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "Goal Completion Gate"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"meta": { "title": "Learn Claude Code", "description": "0 から 1 へ nano Claude Code-like agent を構築し、毎回 1 つの仕組みを追加" },
|
||||
"nav": { "home": "ホーム", "timeline": "学習パス", "compare": "バージョン比較", "layers": "アーキテクチャ層", "github": "GitHub" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "0 から 1 へ nano Claude Code-like agent を構築し、毎回 1 つの仕組みを追加", "start": "学習を始める", "core_pattern": "コアパターン", "core_pattern_desc": "すべての AI コーディングエージェントは同じループを共有する:モデルを呼び出し、ツールを実行し、結果を返す。Harness はその周囲にポリシー、権限、記憶、協調、ライフサイクル制御を加える。", "learning_path": "学習パス", "learning_path_desc": "21の段階的セッション、シンプルなループから決定的な編成と目標完了まで", "layers_title": "アーキテクチャ層", "layers_desc": "5つの直交する関心事が完全なエージェントを構成", "loc": "行", "learn_more": "詳細を見る", "versions_in_layer": "バージョン", "message_flow": "メッセージの増加", "message_flow_desc": "エージェントループ実行時のメッセージ配列の成長を観察" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "0 から 1 へ nano Claude Code-like agent を構築し、毎回 1 つの仕組みを追加", "start": "学習を始める", "core_pattern": "コアパターン", "core_pattern_desc": "すべての AI コーディングエージェントは同じループを共有する:モデルを呼び出し、ツールを実行し、結果を返す。Harness はその周囲にポリシー、権限、記憶、協調、ライフサイクル制御を加える。", "learning_path": "学習パス", "learning_path_desc": "19の段階的セッション、シンプルなループから決定的な編成と目標完了まで", "layers_title": "アーキテクチャ層", "layers_desc": "5つの直交する関心事が完全なエージェントを構成", "loc": "行", "learn_more": "詳細を見る", "versions_in_layer": "バージョン", "message_flow": "メッセージの増加", "message_flow_desc": "エージェントループ実行時のメッセージ配列の成長を観察" },
|
||||
"version": { "loc": "行のコード", "tools": "ツール", "new": "新規", "prev": "前のバージョン", "next": "次のバージョン", "view_source": "ソースを見る", "view_diff": "差分を見る", "design_decisions": "設計判断", "whats_new": "新機能", "tutorial": "チュートリアル", "simulator": "エージェントループシミュレーター", "execution_flow": "実行フロー", "architecture": "アーキテクチャ", "concept_viz": "コンセプト可視化", "alternatives": "検討された代替案", "tab_learn": "学習", "tab_simulate": "シミュレーション", "tab_code": "ソースコード", "tab_deep_dive": "詳細分析" },
|
||||
"sim": { "play": "再生", "pause": "一時停止", "step": "ステップ", "reset": "リセット", "speed": "速度", "step_of": "/" },
|
||||
"timeline": { "title": "学習パス", "subtitle": "s01からs21へ:段階的エージェント Harness 設計", "layer_legend": "レイヤー凡例", "loc_growth": "コード量の推移", "learn_more": "詳細を見る" },
|
||||
"timeline": { "title": "学習パス", "subtitle": "s01からs19へ:段階的エージェント Harness 設計", "layer_legend": "レイヤー凡例", "loc_growth": "コード量の推移", "learn_more": "詳細を見る" },
|
||||
"layers": {
|
||||
"title": "アーキテクチャ層",
|
||||
"subtitle": "5つの直交する関心事が完全なエージェントを構成",
|
||||
@@ -12,7 +12,7 @@
|
||||
"planning": "作業の組織化。シンプルなToDoリストからエージェント間で共有される依存関係対応タスクボードまで。",
|
||||
"memory": "コンテキスト制限内での記憶保持。圧縮戦略によりエージェントが一貫性を失わずに無限に作業可能。",
|
||||
"concurrency": "ノンブロッキング実行。バックグラウンドスレッドと通知バスによる並列作業。",
|
||||
"collaboration": "マルチエージェント連携。チーム、メッセージング、自律的に考えるチームメイト。"
|
||||
"collaboration": "マルチエージェント連携。チーム、メッセージング、原子的なタスク認領、タスクに紐付く worktree ディレクトリ。"
|
||||
},
|
||||
"compare": {
|
||||
"title": "バージョン比較",
|
||||
@@ -53,13 +53,11 @@
|
||||
"s12": "タスクシステム",
|
||||
"s13": "バックグラウンドタスク",
|
||||
"s14": "Cron スケジューラー",
|
||||
"s15": "Agent Teams",
|
||||
"s16": "自律エージェント",
|
||||
"s17": "Worktree 分離",
|
||||
"s18": "MCP ツール",
|
||||
"s19": "Integrated Harness",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "Goal Loop"
|
||||
"s15": "Agent Team Runtime",
|
||||
"s16": "MCP ツール",
|
||||
"s17": "Integrated Harness",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "Goal Loop"
|
||||
},
|
||||
"layer_labels": {
|
||||
"tools": "ツールと実行",
|
||||
@@ -83,12 +81,10 @@
|
||||
"s12": "タスクボード依存関係",
|
||||
"s13": "バックグラウンドタスクレーン",
|
||||
"s14": "Cron スケジューラー",
|
||||
"s15": "Agent Teams と協調プロトコル",
|
||||
"s16": "自律エージェントサイクル",
|
||||
"s17": "Worktree タスク分離",
|
||||
"s18": "MCP ツールブリッジ",
|
||||
"s19": "Integrated Harness のターン",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "目標完了ゲート"
|
||||
"s15": "Team Runtime:メッセージ・認領・ディレクトリ紐付け",
|
||||
"s16": "MCP ツールブリッジ",
|
||||
"s17": "Integrated Harness のターン",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "目標完了ゲート"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"meta": { "title": "Learn Claude Code", "description": "从 0 到 1 构建 nano Claude Code-like agent,每次只加一个机制" },
|
||||
"nav": { "home": "首页", "timeline": "学习路径", "compare": "版本对比", "layers": "架构层", "github": "GitHub" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "从 0 到 1 构建 nano Claude Code-like agent,每次只加一个机制", "start": "开始学习", "core_pattern": "核心模式", "core_pattern_desc": "所有 AI 编程 Agent 共享同一个循环:调用模型、执行工具、回传结果。Harness 在循环周围加入策略、权限、记忆、协作与生命周期控制。", "learning_path": "学习路径", "learning_path_desc": "21 个渐进式课程,从简单循环到确定性编排与目标闭环", "layers_title": "架构层次", "layers_desc": "五个正交关注点组合成完整的 Agent", "loc": "行", "learn_more": "了解更多", "versions_in_layer": "个版本", "message_flow": "消息增长", "message_flow_desc": "观察 Agent 循环执行时消息数组的增长" },
|
||||
"home": { "hero_title": "Learn Claude Code", "hero_subtitle": "从 0 到 1 构建 nano Claude Code-like agent,每次只加一个机制", "start": "开始学习", "core_pattern": "核心模式", "core_pattern_desc": "所有 AI 编程 Agent 共享同一个循环:调用模型、执行工具、回传结果。Harness 在循环周围加入策略、权限、记忆、协作与生命周期控制。", "learning_path": "学习路径", "learning_path_desc": "19 个渐进式课程,从简单循环到确定性编排与目标闭环", "layers_title": "架构层次", "layers_desc": "五个正交关注点组合成完整的 Agent", "loc": "行", "learn_more": "了解更多", "versions_in_layer": "个版本", "message_flow": "消息增长", "message_flow_desc": "观察 Agent 循环执行时消息数组的增长" },
|
||||
"version": { "loc": "行代码", "tools": "个工具", "new": "新增", "prev": "上一版", "next": "下一版", "view_source": "查看源码", "view_diff": "查看变更", "design_decisions": "设计决策", "whats_new": "新增内容", "tutorial": "教程", "simulator": "Agent 循环模拟器", "execution_flow": "执行流程", "architecture": "架构", "concept_viz": "概念可视化", "alternatives": "替代方案", "tab_learn": "学习", "tab_simulate": "模拟", "tab_code": "源码", "tab_deep_dive": "深入探索" },
|
||||
"sim": { "play": "播放", "pause": "暂停", "step": "单步", "reset": "重置", "speed": "速度", "step_of": "/" },
|
||||
"timeline": { "title": "学习路径", "subtitle": "s01 到 s21:渐进式 Agent Harness 设计", "layer_legend": "层次图例", "loc_growth": "代码量增长", "learn_more": "了解更多" },
|
||||
"timeline": { "title": "学习路径", "subtitle": "s01 到 s19:渐进式 Agent Harness 设计", "layer_legend": "层次图例", "loc_growth": "代码量增长", "learn_more": "了解更多" },
|
||||
"layers": {
|
||||
"title": "架构层次",
|
||||
"subtitle": "五个正交关注点组合成完整的 Agent",
|
||||
@@ -12,7 +12,7 @@
|
||||
"planning": "如何组织工作。从简单的待办列表到跨 Agent 共享的依赖感知任务板。",
|
||||
"memory": "在上下文限制内保持记忆。压缩策略让 Agent 可以无限工作而不失去连贯性。",
|
||||
"concurrency": "非阻塞执行。后台线程和通知总线实现并行工作。",
|
||||
"collaboration": "多 Agent 协作。团队、消息传递和能独立思考的自主队友。"
|
||||
"collaboration": "多 Agent 协作。团队、消息传递、原子任务认领与任务绑定的 worktree 目录。"
|
||||
},
|
||||
"compare": {
|
||||
"title": "版本对比",
|
||||
@@ -53,13 +53,11 @@
|
||||
"s12": "Task System",
|
||||
"s13": "Background Tasks",
|
||||
"s14": "Cron Scheduler",
|
||||
"s15": "Agent Teams",
|
||||
"s16": "Autonomous Agents",
|
||||
"s17": "Worktree Isolation",
|
||||
"s18": "MCP Tools",
|
||||
"s19": "Agent Harness 集成",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "Goal Loop"
|
||||
"s15": "Agent Team Runtime",
|
||||
"s16": "MCP Tools",
|
||||
"s17": "Agent Harness 集成",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "Goal Loop"
|
||||
},
|
||||
"layer_labels": {
|
||||
"tools": "工具与执行",
|
||||
@@ -83,12 +81,10 @@
|
||||
"s12": "任务看板依赖",
|
||||
"s13": "Background Task Lanes",
|
||||
"s14": "Cron Scheduler",
|
||||
"s15": "Agent Teams 与协作协议",
|
||||
"s16": "Autonomous Agent Cycle",
|
||||
"s17": "Worktree Task Isolation",
|
||||
"s18": "MCP Tool Bridge",
|
||||
"s19": "Agent Harness 集成流程",
|
||||
"s20": "Workflow Runtime",
|
||||
"s21": "目标完成闸门"
|
||||
"s15": "团队运行时:消息、认领与目录绑定",
|
||||
"s16": "MCP Tool Bridge",
|
||||
"s17": "Agent Harness 集成流程",
|
||||
"s18": "Workflow Runtime",
|
||||
"s19": "目标完成闸门"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ export const VERSION_ORDER = [
|
||||
"s17",
|
||||
"s18",
|
||||
"s19",
|
||||
"s20",
|
||||
"s21",
|
||||
] as const;
|
||||
|
||||
export const LEARNING_PATH = VERSION_ORDER;
|
||||
@@ -149,60 +147,44 @@ export const VERSION_META: Record<string, {
|
||||
prevVersion: "s13",
|
||||
},
|
||||
s15: {
|
||||
title: "Agent Teams",
|
||||
subtitle: "Persistent Teammates and Coordination Protocols",
|
||||
coreAddition: "Team runtime and typed protocols",
|
||||
keyInsight: "A Lead can coordinate persistent teammates when message delivery, approval, and shutdown belong to the runtime.",
|
||||
title: "Agent Team Runtime",
|
||||
subtitle: "Persistent Teammates, Atomic Claims, Task-Bound Worktrees",
|
||||
coreAddition: "Team runtime with task-bound worktrees",
|
||||
keyInsight: "Persistent teammates can reliably discover and execute parallel work when the runtime owns messaging, atomic claims, and task-bound working directories.",
|
||||
layer: "collaboration",
|
||||
prevVersion: "s14",
|
||||
},
|
||||
s16: {
|
||||
title: "Autonomous Agents",
|
||||
subtitle: "Check the Board, Claim the Task",
|
||||
coreAddition: "Autonomous task claiming",
|
||||
keyInsight: "Idle teammates can discover ready work when claiming is atomic and respects task dependencies.",
|
||||
layer: "collaboration",
|
||||
prevVersion: "s15",
|
||||
},
|
||||
s17: {
|
||||
title: "Worktree Isolation",
|
||||
subtitle: "Separate Directories, No Conflicts",
|
||||
coreAddition: "Worktree lifecycle",
|
||||
keyInsight: "Parallel agents need isolated filesystems as much as isolated conversations.",
|
||||
layer: "collaboration",
|
||||
prevVersion: "s16",
|
||||
},
|
||||
s18: {
|
||||
title: "MCP Tools",
|
||||
subtitle: "External Tools, Standard Protocol",
|
||||
coreAddition: "MCP tool bridge",
|
||||
keyInsight: "External services can become agent tools through a standard discovery and call protocol.",
|
||||
layer: "collaboration",
|
||||
prevVersion: "s17",
|
||||
prevVersion: "s15",
|
||||
},
|
||||
s19: {
|
||||
s17: {
|
||||
title: "Integrated Harness",
|
||||
subtitle: "Many Mechanisms, One Loop",
|
||||
coreAddition: "Integrated harness",
|
||||
keyInsight: "The integrated harness is still one loop, surrounded by the systems introduced across the course.",
|
||||
layer: "collaboration",
|
||||
prevVersion: "s18",
|
||||
prevVersion: "s16",
|
||||
},
|
||||
s20: {
|
||||
s18: {
|
||||
title: "Workflow Runtime",
|
||||
subtitle: "Scripts Own Fixed Orchestration",
|
||||
coreAddition: "Resumable workflow runtime",
|
||||
keyInsight: "When orchestration has a fixed shape, code can make it parallel, deterministic, and resumable.",
|
||||
layer: "concurrency",
|
||||
prevVersion: "s19",
|
||||
prevVersion: "s17",
|
||||
},
|
||||
s21: {
|
||||
s19: {
|
||||
title: "Goal Loop",
|
||||
subtitle: "Trusted Evidence Decides When to Stop",
|
||||
subtitle: "Independent Evaluation Decides When to Stop",
|
||||
coreAddition: "Goal completion gate",
|
||||
keyInsight: "A durable goal keeps the loop working until trusted evidence satisfies an explicit condition.",
|
||||
keyInsight: "A durable goal keeps the loop working until an independent evaluator finds the completion condition satisfied in the conversation.",
|
||||
layer: "planning",
|
||||
prevVersion: "s20",
|
||||
prevVersion: "s18",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -217,7 +199,7 @@ export const LAYERS = [
|
||||
id: "planning" as const,
|
||||
label: "Planning & Control",
|
||||
color: "#10B981",
|
||||
versions: ["s05", "s06", "s07", "s10", "s11", "s21"],
|
||||
versions: ["s05", "s06", "s07", "s10", "s11", "s19"],
|
||||
},
|
||||
{
|
||||
id: "memory" as const,
|
||||
@@ -229,12 +211,12 @@ export const LAYERS = [
|
||||
id: "concurrency" as const,
|
||||
label: "Concurrency & Scheduling",
|
||||
color: "#F59E0B",
|
||||
versions: ["s13", "s14", "s20"],
|
||||
versions: ["s13", "s14", "s18"],
|
||||
},
|
||||
{
|
||||
id: "collaboration" as const,
|
||||
label: "Multi-Agent Platform",
|
||||
color: "#EF4444",
|
||||
versions: ["s12", "s15", "s16", "s17", "s18", "s19"],
|
||||
versions: ["s12", "s15", "s16", "s17"],
|
||||
},
|
||||
] as const;
|
||||
|
||||
Reference in New Issue
Block a user