refactor: streamline the course to 17 lessons

This commit is contained in:
Haoran
2026-08-12 03:02:42 +08:00
parent ab35e59672
commit 7e2f2fd99b
250 changed files with 12179 additions and 18653 deletions

View File

@@ -23,8 +23,6 @@ import s14Annotations from "@/data/annotations/s14.json";
import s15Annotations from "@/data/annotations/s15.json";
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";
interface Decision {
id: string;
@@ -58,8 +56,6 @@ const ANNOTATIONS: Record<string, AnnotationFile> = {
s15: s15Annotations as AnnotationFile,
s16: s16Annotations as AnnotationFile,
s17: s17Annotations as AnnotationFile,
s18: s18Annotations as AnnotationFile,
s19: s19Annotations as AnnotationFile,
};
interface DesignDecisionsProps {

View File

@@ -26,8 +26,6 @@ const scenarioModules: Record<string, () => Promise<{ default: Scenario }>> = {
s15: () => import("@/data/scenarios/s15.json") as Promise<{ default: Scenario }>,
s16: () => import("@/data/scenarios/s16.json") as 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 }>,
};
interface AgentLoopSimulatorProps {

View File

@@ -13,17 +13,15 @@ const visualizations: Record<
s04: lazy(() => import("./s04-hooks")),
s05: lazy(() => import("./s03-todo-write")),
s06: lazy(() => import("./s06-subagent")),
s07: lazy(() => import("./s05-skill-loading")),
s08: lazy(() => import("./s06-context-compact")),
s07: lazy(() => import("./s07-skill-loading")),
s08: lazy(() => import("./s08-context-compact")),
s09: lazy(() => import("./s09-memory")),
s10: lazy(() => import("./s10-system-prompt")),
s11: lazy(() => import("./s11-error-recovery")),
s12: lazy(() => import("./s07-task-system")),
s13: lazy(() => import("./s08-background-tasks")),
s14: lazy(() => import("./s14-cron-scheduler")),
s15: lazy(() => import("./s15-team-runtime")),
s16: lazy(() => import("./s16-mcp-tools")),
s17: lazy(() => import("./s17-integrated-harness")),
s10: lazy(() => import("./s10-task-system")),
s11: lazy(() => import("./s11-background-tasks")),
s12: lazy(() => import("./s12-cron-scheduler")),
s13: lazy(() => import("./s13-team-runtime")),
s14: lazy(() => import("./s14-mcp-tools")),
s15: lazy(() => import("./s15-integrated-harness")),
};
export function SessionVisualization({ version }: { version: string }) {

View File

@@ -7,90 +7,84 @@ import { StepControls } from "@/components/visualizations/shared/step-controls";
interface SkillEntry {
name: string;
summary: string;
fullTokens: number;
content: string[];
}
const SKILLS: SkillEntry[] = [
{
name: "/commit",
summary: "Create git commits following repo conventions",
fullTokens: 320,
name: "code-review",
summary: "Review code for bugs, security, and maintainability",
content: [
"1. Run git status + git diff to see changes",
"2. Analyze all staged changes and draft message",
"3. Create commit with Co-Authored-By trailer",
"4. Run git status after commit to verify",
"# Code Review Skill",
"1. Inspect the change and its surrounding code",
"2. Prioritize bugs and behavioral regressions",
"3. Report missing tests and residual risk",
],
},
{
name: "/review-pr",
summary: "Review pull requests for bugs and style",
fullTokens: 480,
name: "pdf",
summary: "Read, create, and modify PDF files",
content: [
"1. Fetch PR diff via gh pr view",
"2. Analyze changes file by file for issues",
"3. Check for bugs, security, and style problems",
"4. Post review comments with gh pr review",
"# PDF Processing Skill",
"1. Choose text extraction or rendered inspection",
"2. Preserve page order and layout where needed",
"3. Verify the produced PDF before returning it",
],
},
{
name: "/test",
summary: "Run and analyze test suites",
fullTokens: 290,
name: "agent-builder",
summary: "Design and build agents for a target domain",
content: [
"1. Detect test framework from package.json",
"2. Run test suite and capture output",
"3. Analyze failures and suggest fixes",
"4. Re-run after applying fixes",
"# Agent Builder Skill",
"1. Define the agent's task and boundaries",
"2. Select tools and state",
"3. Test the complete loop",
],
},
{
name: "/deploy",
summary: "Deploy application to target environment",
fullTokens: 350,
name: "mcp-builder",
summary: "Build MCP servers and expose external tools",
content: [
"1. Verify all tests pass before deploy",
"2. Build production bundle",
"3. Push to deployment target via CI",
"4. Verify health check on deployed URL",
"# MCP Server Building Skill",
"1. Define tool schemas",
"2. Connect handlers to external services",
"3. Verify discovery and tool calls",
],
},
];
const TOKEN_STATES = [120, 120, 440, 440, 780, 780];
const MAX_TOKEN_DISPLAY = 1000;
const LOADED_STATES = [0, 0, 1, 1, 2, 2];
const STEPS = [
{
title: "Layer 1: Compact Summaries",
title: "Scan the Catalog",
description:
"All skills are summarized in the system prompt. Compact, always present.",
"Startup adds skill names and descriptions to the system prompt.",
},
{
title: "Skill Invocation",
title: "A Specialized Task",
description:
'The model recognizes a skill invocation and triggers the Skill tool.',
"The user asks for work covered by one of the listed skills.",
},
{
title: "Layer 2: Full Injection",
title: "Load the Skill",
description:
"The full skill instructions are injected as a tool_result, not into the system prompt.",
},
{
title: "In Context Now",
title: "Follow the Instructions",
description:
"The detailed instructions appear as if a tool returned them. The model follows them precisely.",
},
{
title: "Stack Skills",
title: "Load Another Skill",
description:
"Multiple skills can be loaded. Only summaries are permanent; full content comes and goes.",
"A later task can load a different SKILL.md through the same tool.",
},
{
title: "Two-Layer Architecture",
title: "Catalog and Full Content",
description:
"Layer 1: always present, tiny. Layer 2: loaded on demand, detailed. Elegant separation.",
"The catalog supports discovery; load_skill returns the selected instructions.",
},
];
@@ -105,7 +99,7 @@ export default function SkillLoading({ title }: { title?: string }) {
toggleAutoPlay,
} = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2500 });
const tokenCount = TOKEN_STATES[currentStep];
const loadedCount = LOADED_STATES[currentStep];
const highlightedSkill = currentStep >= 1 && currentStep <= 3 ? 0 : currentStep >= 4 ? 1 : -1;
const showFirstContent = currentStep >= 2;
const showSecondContent = currentStep >= 4;
@@ -179,10 +173,10 @@ export default function SkillLoading({ title }: { title?: string }) {
className="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 dark:border-blue-800 dark:bg-blue-950/30"
>
<span className="text-xs text-blue-600 dark:text-blue-400">
User types:
User asks:
</span>
<code className="rounded bg-blue-100 px-2 py-0.5 text-xs font-bold text-blue-800 dark:bg-blue-900/50 dark:text-blue-200">
/commit
Review this change for bugs and regressions.
</code>
</motion.div>
)}
@@ -194,10 +188,10 @@ export default function SkillLoading({ title }: { title?: string }) {
className="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 dark:border-blue-800 dark:bg-blue-950/30"
>
<span className="text-xs text-blue-600 dark:text-blue-400">
User types:
User asks:
</span>
<code className="rounded bg-blue-100 px-2 py-0.5 text-xs font-bold text-blue-800 dark:bg-blue-900/50 dark:text-blue-200">
/review-pr
Extract the tables from this PDF.
</code>
</motion.div>
)}
@@ -239,7 +233,7 @@ export default function SkillLoading({ title }: { title?: string }) {
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
<span className="text-xs font-bold text-blue-700 dark:text-blue-300">
SKILL.md: /commit
SKILL.md: code-review
</span>
</div>
<span className="rounded bg-blue-100 px-1.5 py-0.5 font-mono text-[10px] text-blue-600 dark:bg-blue-900/40 dark:text-blue-300">
@@ -281,7 +275,7 @@ export default function SkillLoading({ title }: { title?: string }) {
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-purple-500" />
<span className="text-xs font-bold text-purple-700 dark:text-purple-300">
SKILL.md: /review-pr
SKILL.md: pdf
</span>
</div>
<span className="rounded bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] text-purple-600 dark:bg-purple-900/40 dark:text-purple-300">
@@ -318,7 +312,7 @@ export default function SkillLoading({ title }: { title?: string }) {
>
The Skill tool returns content as a tool_result message.
The model sees it in context and follows the instructions.
No system prompt bloat.
The full file is now part of the message history.
</motion.div>
)}
</AnimatePresence>
@@ -334,18 +328,18 @@ export default function SkillLoading({ title }: { title?: string }) {
>
<div className="flex-1 rounded border border-zinc-200 bg-zinc-50 p-2 text-center dark:border-zinc-700 dark:bg-zinc-800">
<div className="text-[10px] font-semibold text-zinc-500 dark:text-zinc-400">
LAYER 1
CATALOG
</div>
<div className="text-xs text-zinc-600 dark:text-zinc-300">
Always present, ~120 tokens
Names and descriptions in the system prompt
</div>
</div>
<div className="flex-1 rounded border border-blue-200 bg-blue-50 p-2 text-center dark:border-blue-700 dark:bg-blue-900/20">
<div className="text-[10px] font-semibold text-blue-500 dark:text-blue-400">
LAYER 2
FULL CONTENT
</div>
<div className="text-xs text-blue-600 dark:text-blue-300">
On demand, ~300-500 tokens each
Selected SKILL.md returned by load_skill
</div>
</div>
</motion.div>
@@ -353,10 +347,10 @@ export default function SkillLoading({ title }: { title?: string }) {
</AnimatePresence>
</div>
{/* Token Gauge (vertical bar on the right) */}
{/* Loaded skill count */}
<div className="flex w-16 flex-col items-center">
<div className="mb-1 text-center font-mono text-[10px] text-zinc-400">
Tokens
Loaded
</div>
<div
className="relative w-8 overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800"
@@ -364,25 +358,21 @@ export default function SkillLoading({ title }: { title?: string }) {
>
<motion.div
animate={{
height: `${(tokenCount / MAX_TOKEN_DISPLAY) * 100}%`,
height: `${loadedCount * 35}%`,
}}
transition={{ duration: 0.5 }}
className={`absolute bottom-0 w-full rounded-full ${
tokenCount > 600
? "bg-amber-500"
: tokenCount > 300
? "bg-blue-500"
: "bg-emerald-500"
loadedCount > 1 ? "bg-blue-500" : "bg-emerald-500"
}`}
/>
</div>
<motion.div
key={tokenCount}
key={loadedCount}
initial={{ scale: 0.8 }}
animate={{ scale: 1 }}
className="mt-2 text-center font-mono text-xs font-semibold text-zinc-600 dark:text-zinc-300"
>
{tokenCount}
{loadedCount}
</motion.div>
</div>
</div>

View File

@@ -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/s19.",
body: "Run npm run build, then browser-check /zh/s09 and /zh/s17.",
},
];

View File

@@ -1,260 +0,0 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { Boxes, Brain, CheckCircle2, FileText, KeyRound, Library, Rocket, Wrench } from "lucide-react";
import { StepControls } from "@/components/visualizations/shared/step-controls";
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
import { cn } from "@/lib/utils";
const STEPS = [
{
title: "Runtime State Arrives",
desc: "The prompt is not a fixed paragraph; it starts from workspace, tools, memory, and skills.",
mode: "state",
},
{
title: "Section Shelf Selects Owners",
desc: "Each subsystem owns one prompt section, so a bad rule has a place to debug.",
mode: "sections",
},
{
title: "Context Key Checks the Cache",
desc: "The same runtime state produces the same deterministic cache key.",
mode: "cache-miss",
},
{
title: "Prompt Is Assembled",
desc: "Selected sections are joined into one system prompt that the LLM can read.",
mode: "assemble",
},
{
title: "Same Key Reuses the Prompt",
desc: "If nothing changed, the runtime skips assembly and reuses the cached prompt.",
mode: "cache-hit",
},
{
title: "LLM Sees the Built Prompt",
desc: "The model receives a traceable product of runtime state, not a stale hardcoded string.",
mode: "llm",
},
] as const;
const SOURCES = [
{ id: "workspace", label: "workspace", value: "/repo", icon: <Boxes size={16} />, tone: "blue" },
{ id: "tools", label: "tools", value: "bash, read_file", icon: <Wrench size={16} />, tone: "emerald" },
{ id: "memory", label: "memory", value: "enabled", icon: <Brain size={16} />, tone: "amber" },
{ id: "skills", label: "skills", value: "code-review", icon: <Library size={16} />, tone: "violet" },
] as const;
const SECTIONS = [
{ id: "identity", title: "identity", body: "You are a helpful coding agent.", owner: "core" },
{ id: "tools", title: "tools", body: "Available tools: bash, read_file.", owner: "tool registry" },
{ id: "workspace", title: "workspace", body: "Current workspace: /repo.", owner: "runtime" },
{ id: "memory", title: "memory + skills", body: "Load memory index and code-review skill.", owner: "context loader" },
] as const;
type StepMode = (typeof STEPS)[number]["mode"];
type Tone = "blue" | "emerald" | "amber" | "violet" | "zinc";
function toneClass(tone: Tone, active = true) {
if (!active) return "border-zinc-200 bg-white text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200";
if (tone === "blue") return "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-200";
if (tone === "emerald") return "border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-200";
if (tone === "amber") return "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200";
if (tone === "violet") return "border-violet-200 bg-violet-50 text-violet-800 dark:border-violet-900 dark:bg-violet-950/40 dark:text-violet-200";
return "border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200";
}
function Surface({
title,
icon,
active,
children,
}: {
title: string;
icon: React.ReactNode;
active: boolean;
children: React.ReactNode;
}) {
return (
<div
className={cn(
"min-w-0 rounded-lg border p-4 transition-colors",
active
? "border-blue-300 bg-blue-50 dark:border-blue-900 dark:bg-blue-950/30"
: "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
)}
>
<div className="mb-4 flex min-w-0 items-center gap-3 text-lg font-semibold text-zinc-900 dark:text-zinc-100">
<span
className={cn(
"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg",
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 text-wrap">{title}</span>
</div>
{children}
</div>
);
}
function SourceCard({
source,
active,
}: {
source: (typeof SOURCES)[number];
active: boolean;
}) {
return (
<motion.div layout animate={active ? { y: -1 } : { y: 0 }} className={cn("rounded-lg border p-3", toneClass(source.tone as Tone, active))}>
<div className="mb-2 flex items-center gap-2 text-sm font-semibold">
{source.icon}
{source.label}
</div>
<code className="block min-w-0 whitespace-pre-wrap break-words rounded bg-white/70 px-2 py-1 font-mono text-xs dark:bg-zinc-950/30">
{source.value}
</code>
</motion.div>
);
}
function SectionCard({
section,
active,
assembled,
}: {
section: (typeof SECTIONS)[number];
active: boolean;
assembled: boolean;
}) {
return (
<motion.div
layout
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
className={cn(
"min-w-0 rounded-lg border p-3",
active || assembled ? toneClass("emerald") : "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
)}
>
<div className="mb-1 flex min-w-0 items-center justify-between gap-2">
<div className="min-w-0 break-words font-mono text-sm font-semibold leading-snug">{section.title}</div>
{(active || assembled) && <CheckCircle2 size={15} className="shrink-0" />}
</div>
<div className="mb-2 text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">owner: {section.owner}</div>
<div className="text-sm leading-relaxed text-zinc-700 dark:text-zinc-200">{section.body}</div>
</motion.div>
);
}
function CachePanel({ mode }: { mode: StepMode }) {
const isHit = mode === "cache-hit";
const isActive = mode === "cache-miss" || mode === "cache-hit";
return (
<div className={cn("rounded-lg border p-3", toneClass(isHit ? "emerald" : "amber", isActive))}>
<div className="mb-2 flex items-center gap-2 text-sm font-semibold">
<KeyRound size={16} />
context key
</div>
<code className="block min-w-0 whitespace-pre-wrap break-words rounded bg-white/70 p-2 font-mono text-xs dark:bg-zinc-950/30">
json.dumps(context, sort_keys=True)
</code>
<div className="mt-2 text-sm font-semibold">{isHit ? "cache hit: reuse prompt" : isActive ? "cache miss: assemble sections" : "waiting for state"}</div>
</div>
);
}
function PromptPreview({ mode }: { mode: StepMode }) {
const assembled = mode === "assemble" || mode === "cache-hit" || mode === "llm";
if (!assembled) {
return (
<div className="rounded-lg border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500 dark:border-zinc-700 dark:text-zinc-400">
prompt not built yet
</div>
);
}
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className="space-y-2">
{SECTIONS.map((section) => (
<div key={section.id} className="rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
<div className="mb-1 font-mono text-xs font-semibold text-blue-700 dark:text-blue-300">[{section.title}]</div>
<div className="text-sm leading-relaxed text-zinc-700 dark:text-zinc-200">{section.body}</div>
</div>
))}
<div className={cn("rounded-xl border p-4", toneClass(mode === "llm" ? "blue" : "zinc"))}>
<div className="mb-2 flex items-center gap-2 text-base font-semibold">
<Rocket size={17} />
{mode === "llm" ? "sent to LLM" : "system prompt ready"}
</div>
<div className="text-sm leading-relaxed">Traceable prompt text, assembled from named runtime owners.</div>
</div>
</motion.div>
);
}
export default function SystemPromptVisualization({ title }: { title?: string }) {
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2600 });
const current = STEPS[vis.currentStep];
const mode = current.mode;
const sourceActive = mode === "state" || mode === "sections" || mode === "cache-miss";
const sectionsActive = mode === "sections" || mode === "assemble";
const promptActive = mode === "assemble" || mode === "cache-hit" || mode === "llm";
return (
<section className="min-h-[500px] space-y-4">
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">{title || "Runtime Prompt Assembly"}</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-[0.95fr_1.1fr_0.95fr]">
<Surface title="Runtime context" icon={<Boxes size={20} />} active={sourceActive}>
<div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-1">
{SOURCES.map((source) => (
<SourceCard key={source.id} source={source} active={sourceActive} />
))}
</div>
</Surface>
<Surface title="Section shelf + cache" icon={<FileText size={20} />} active={sectionsActive || mode === "cache-miss" || mode === "cache-hit"}>
<div className="space-y-3">
<div className="grid gap-2 sm:grid-cols-2">
{SECTIONS.map((section) => (
<SectionCard key={section.id} section={section} active={sectionsActive} assembled={promptActive} />
))}
</div>
<CachePanel mode={mode} />
</div>
</Surface>
<Surface title="System prompt" icon={<Rocket size={20} />} active={promptActive}>
<AnimatePresence mode="wait">
<PromptPreview key={mode} mode={mode} />
</AnimatePresence>
</Surface>
</div>
<div className="mt-3 rounded-lg border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm leading-relaxed text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800/70 dark:text-zinc-300">
Beginner rule: system prompts should be assembled from named runtime facts, then cached only when those facts are unchanged.
</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>
);
}

View File

@@ -1,347 +0,0 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { Activity, AlertTriangle, Gauge, History, Repeat2, RotateCcw, ShieldCheck, TimerReset, Workflow } from "lucide-react";
import { StepControls } from "@/components/visualizations/shared/step-controls";
import { useSteppedVisualization } from "@/hooks/useSteppedVisualization";
import { cn } from "@/lib/utils";
const STEPS = [
{
title: "Normal Call Still Comes First",
desc: "The runtime starts with a regular LLM call and only enters recovery when a specific failure appears.",
mode: "normal",
},
{
title: "max_tokens Means Output Was Cut Off",
desc: "First recovery is to retry with a larger budget before adding any synthetic continuation message.",
mode: "max-tokens",
},
{
title: "prompt_too_long Means Context Must Shrink",
desc: "The runtime performs reactive compact once, then retries the same task with a smaller message list.",
mode: "prompt-too-long",
},
{
title: "429 Means Wait, Then Retry",
desc: "Rate limits use exponential backoff with jitter so retries do not stampede the provider.",
mode: "rate-limit",
},
{
title: "Repeated 529 Can Switch Models",
desc: "Provider overload increments RecoveryState and can move to a fallback model after repeated failures.",
mode: "overloaded",
},
{
title: "Recovered Calls Return to the Loop",
desc: "Each recovery path is bounded, inspectable, and eventually returns to the normal tool loop or exits cleanly.",
mode: "summary",
},
] as const;
const CASES = [
{
id: "max-tokens",
label: "max_tokens",
symptom: "model stopped mid-answer",
action: "8K -> 64K, retry same request",
state: "token escalated once",
tone: "amber",
},
{
id: "prompt-too-long",
label: "prompt_too_long",
symptom: "context too large",
action: "reactive_compact(messages), retry once",
state: "compact retry used",
tone: "orange",
},
{
id: "rate-limit",
label: "429",
symptom: "rate limited",
action: "backoff + jitter, max 10 retries",
state: "retry attempt counted",
tone: "blue",
},
{
id: "overloaded",
label: "529",
symptom: "provider overloaded",
action: "backoff; 3 consecutive -> fallback model",
state: "consecutive_529 tracked",
tone: "red",
},
] as const;
type StepMode = (typeof STEPS)[number]["mode"];
type CaseId = (typeof CASES)[number]["id"];
type Tone = "amber" | "orange" | "blue" | "red" | "emerald" | "zinc";
function toneClass(tone: Tone, active = true) {
if (!active) return "border-zinc-200 bg-white text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200";
if (tone === "amber") return "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200";
if (tone === "orange") return "border-orange-200 bg-orange-50 text-orange-800 dark:border-orange-900 dark:bg-orange-950/40 dark:text-orange-200";
if (tone === "blue") return "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-200";
if (tone === "red") return "border-red-200 bg-red-50 text-red-800 dark:border-red-900 dark:bg-red-950/40 dark:text-red-200";
if (tone === "emerald") return "border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-200";
return "border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200";
}
function activeCase(mode: StepMode): CaseId | null {
if (mode === "max-tokens") return "max-tokens";
if (mode === "prompt-too-long") return "prompt-too-long";
if (mode === "rate-limit") return "rate-limit";
if (mode === "overloaded") return "overloaded";
return null;
}
function Surface({
title,
icon,
active,
children,
}: {
title: string;
icon: React.ReactNode;
active: boolean;
children: React.ReactNode;
}) {
return (
<div
className={cn(
"min-w-0 rounded-lg border p-4 transition-colors",
active
? "border-red-300 bg-red-50 dark:border-red-900 dark:bg-red-950/30"
: "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
)}
>
<div className="mb-4 flex min-w-0 items-center gap-3 text-lg font-semibold text-zinc-900 dark:text-zinc-100">
<span
className={cn(
"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg",
active ? "bg-red-500 text-white" : "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300"
)}
>
{icon}
</span>
<span className="min-w-0 text-wrap">{title}</span>
</div>
{children}
</div>
);
}
function CaseCard({
item,
active,
muted,
}: {
item: (typeof CASES)[number];
active: boolean;
muted: boolean;
}) {
return (
<motion.div
layout
animate={active ? { y: -1 } : { y: 0 }}
className={cn(
"min-w-0 rounded-lg border p-3",
active ? toneClass(item.tone as Tone) : "border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
muted && "opacity-45"
)}
>
<div className="mb-2 flex min-w-0 items-center justify-between gap-2">
<div className="min-w-0 font-mono text-sm font-semibold">{item.label}</div>
{active && <AlertTriangle size={15} className="shrink-0" />}
</div>
<div className="text-sm leading-relaxed text-zinc-700 dark:text-zinc-200">{item.symptom}</div>
<div className="mt-2 rounded bg-white/70 px-2 py-1 text-xs leading-relaxed dark:bg-zinc-950/30">{item.action}</div>
</motion.div>
);
}
function RecoveryStatePanel({ mode }: { mode: StepMode }) {
const values = {
token: mode === "max-tokens" || mode === "summary" ? "64K used" : "8K",
compact: mode === "prompt-too-long" || mode === "summary" ? "used once" : "unused",
retry: mode === "rate-limit" || mode === "overloaded" || mode === "summary" ? "counting" : "0",
model: mode === "overloaded" ? "fallback ready" : "primary",
};
return (
<div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-1">
{[
["max_tokens", values.token],
["reactive_compact", values.compact],
["retry_attempt", values.retry],
["current_model", values.model],
].map(([label, value]) => (
<div key={label} className="min-w-0 rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
<div className="mb-1 font-mono text-xs text-zinc-500 dark:text-zinc-400">{label}</div>
<div className="break-words text-sm font-semibold text-zinc-900 dark:text-zinc-100">{value}</div>
</div>
))}
</div>
);
}
function ActionPanel({ mode }: { mode: StepMode }) {
if (mode === "normal") {
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className={cn("rounded-xl border p-4", toneClass("emerald"))}>
<div className="mb-2 flex items-center gap-2 text-base font-semibold">
<ShieldCheck size={17} />
normal tool loop
</div>
<div className="text-sm leading-relaxed">LLM succeeds, tool_use continues as usual.</div>
</motion.div>
);
}
if (mode === "max-tokens") {
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className={cn("space-y-3 rounded-xl border p-4", toneClass("amber"))}>
<div className="flex items-center gap-2 text-base font-semibold">
<Gauge size={17} />
escalate output budget
</div>
<div className="grid gap-2 sm:grid-cols-2">
<CodePill label="before" value="max_tokens=8000" />
<CodePill label="retry" value="max_tokens=64000" />
</div>
<div className="text-sm leading-relaxed">No fake "continue" user message on the first escalation.</div>
</motion.div>
);
}
if (mode === "prompt-too-long") {
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className={cn("space-y-3 rounded-xl border p-4", toneClass("orange"))}>
<div className="flex items-center gap-2 text-base font-semibold">
<History size={17} />
shrink context, retry once
</div>
<CodePill label="recovery" value="messages = reactive_compact(messages)" />
<div className="text-sm leading-relaxed">If it is still too long after compact, exit cleanly instead of looping forever.</div>
</motion.div>
);
}
if (mode === "rate-limit") {
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className={cn("space-y-3 rounded-xl border p-4", toneClass("blue"))}>
<div className="flex items-center gap-2 text-base font-semibold">
<TimerReset size={17} />
exponential backoff
</div>
<div className="grid grid-cols-3 gap-2 text-center text-xs font-semibold">
{["0.5s", "1s", "2s"].map((delay) => (
<div key={delay} className="rounded bg-white/70 px-2 py-2 dark:bg-zinc-950/30">{delay} + jitter</div>
))}
</div>
<div className="text-sm leading-relaxed">Wait before retrying so the provider has time to recover.</div>
</motion.div>
);
}
if (mode === "overloaded") {
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className={cn("space-y-3 rounded-xl border p-4", toneClass("red"))}>
<div className="flex items-center gap-2 text-base font-semibold">
<RotateCcw size={17} />
fallback model path
</div>
<CodePill label="state" value="consecutive_529 >= 3" />
<CodePill label="action" value="current_model = FALLBACK_MODEL_ID" />
</motion.div>
);
}
return (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} className="space-y-2">
{CASES.map((item) => (
<div key={item.id} className={cn("rounded-lg border p-3", toneClass(item.tone as Tone))}>
<div className="mb-1 text-sm font-semibold">{item.label}</div>
<div className="text-xs leading-relaxed opacity-80">{item.state}</div>
</div>
))}
<div className={cn("rounded-xl border p-4", toneClass("emerald"))}>
<div className="mb-2 flex items-center gap-2 text-base font-semibold">
<Repeat2 size={17} />
continue or exit cleanly
</div>
<div className="text-sm leading-relaxed">Every path has a limit, then returns to the normal loop or stops with an explicit error.</div>
</div>
</motion.div>
);
}
function CodePill({ label, value }: { label: string; value: string }) {
return (
<div className="min-w-0 rounded-lg bg-white/70 p-2 dark:bg-zinc-950/30">
<div className="mb-1 text-[11px] font-semibold uppercase tracking-wide opacity-70">{label}</div>
<code className="block min-w-0 whitespace-pre-wrap break-words font-mono text-xs leading-relaxed">{value}</code>
</div>
);
}
export default function ErrorRecoveryVisualization({ title }: { title?: string }) {
const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2600 });
const current = STEPS[vis.currentStep];
const mode = current.mode;
const active = activeCase(mode);
const isSummary = mode === "summary";
return (
<section className="min-h-[500px] space-y-4">
<h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">{title || "Error Recovery Paths"}</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_0.9fr_1fr]">
<Surface title="Failure inbox" icon={<Activity size={20} />} active={mode !== "normal"}>
<div className="space-y-2">
<div className={cn("rounded-lg border p-3", toneClass("emerald", mode === "normal"))}>
<div className="mb-1 flex items-center gap-2 text-sm font-semibold">
<ShieldCheck size={15} />
success
</div>
<div className="text-sm leading-relaxed">No recovery needed; continue to tool loop.</div>
</div>
{CASES.map((item) => (
<CaseCard key={item.id} item={item} active={active === item.id || isSummary} muted={active !== null && active !== item.id && !isSummary} />
))}
</div>
</Surface>
<Surface title="RecoveryState" icon={<Workflow size={20} />} active={mode !== "normal"}>
<RecoveryStatePanel mode={mode} />
</Surface>
<Surface title="Recovery action" icon={<Repeat2 size={20} />} active>
<AnimatePresence mode="wait">
<ActionPanel key={mode} mode={mode} />
</AnimatePresence>
</Surface>
</div>
<div className="mt-3 rounded-lg border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm leading-relaxed text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800/70 dark:text-zinc-300">
Beginner rule: do not blindly retry; classify the failure, run the smallest recovery, and track whether that recovery was already used.
</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>
);
}

View File

@@ -24,7 +24,7 @@ const STEPS = [
{
title: "Claim Atomically",
desc: "A ready task moves to one owner while the task-store file lock protects the persisted transition.",
event: "task_store_lock: task_...0042 -> backend",
event: "task_store_lock: task_1a2b3c4d -> backend",
},
{
title: "Require and Review a Plan",
@@ -135,7 +135,7 @@ function TaskPanel({ step }: { step: number }) {
<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_1712345678_0042
task_1a2b3c4d
</div>
<div className="mt-1 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
Refactor authentication

View File

@@ -2,45 +2,45 @@
"version": "s10",
"decisions": [
{
"id": "prompt-from-state",
"title": "Model Input Is Built from Runtime State",
"description": "Stable prompt sections and dynamic state are assembled at the model boundary: workspace, available tools, memory, and skills. Model input becomes a product of the runtime rather than a single hardcoded string.",
"alternatives": "A static prompt is easier to inspect, but it goes stale as capabilities change.",
"id": "tasks-as-files",
"title": "Tasks Are Durable JSON Files",
"description": "Each task is persisted under .tasks/ with id, subject, description, status, owner, and blockedBy. The task board survives context compaction and process restarts.",
"alternatives": "In-memory tasks are easier to code, but vanish exactly when long-running coordination needs them most.",
"zh": {
"title": "模型输入由运行时状态构建",
"description": "稳定 prompt section 与动态状态在模型边界组装workspace、可用工具、memory 和 skills。模型输入是运行时的产物而不是单个硬编码字符串。"
"title": "任务是持久 JSON 文件",
"description": "每个任务都持久化在 .tasks/ 下,包含 id、subject、description、status、owner、blockedBy。任务板能跨上下文压缩和进程重启保留。"
},
"ja": {
"title": "モデル入力はランタイム状態から作る",
"description": "安定した prompt section と workspace、利用可能ツール、memory、skills などの動的状態をモデル境界で組み立てます。モデル入力は単一の固定文字列ではなくランタイムの産物です。"
"title": "タスクは永続 JSON ファイル",
"description": "各タスクは .tasks/ に id、subject、description、status、owner、blockedBy を持って保存されます。タスクボードはコンテキスト圧縮や再起動を越えて残ります。"
}
},
{
"id": "deterministic-cache-key",
"title": "A Deterministic Cache Avoids Reassembly",
"description": "The context object is serialized with stable ordering. If the key has not changed, the prompt can be reused safely.",
"alternatives": "Rebuilding every turn is simple, but hides when the prompt actually changed.",
"id": "blockedby-dependencies",
"title": "blockedBy Encodes Ordering",
"description": "A task can only be claimed when all blockedBy dependencies are completed. Missing dependencies are treated as blocked to fail closed.",
"alternatives": "Letting the model remember ordering is fragile and hard for teammates to share.",
"zh": {
"title": "确定性缓存避免重复组装",
"description": "Context 对象用稳定顺序序列化。如果 key 没变,提示词就可以安全复用。"
"title": "blockedBy 编码任务顺序",
"description": "只有所有 blockedBy 依赖都完成时,任务才能被 claim。缺失依赖也被视为阻塞采用 fail closed。"
},
"ja": {
"title": "決定的キャッシュで再組み立てを避ける",
"description": "context オブジェクトを安定した順序でシリアライズします。key が変わらなければプロンプトを安全に再利用できます。"
"title": "blockedBy が順序を表現する",
"description": "blockedBy の依存がすべて完了した時だけタスクを claim できます。存在しない依存もブロック扱いにして fail closed にします。"
}
},
{
"id": "sections-have-owners",
"title": "Prompt Sections Have Owners",
"description": "Identity, tools, workspace, and memory are separate sections. This makes it easier to debug which subsystem injected a bad instruction.",
"alternatives": "Concatenating arbitrary strings works until the prompt grows and no one knows where a rule came from.",
"id": "claim-complete-lifecycle",
"title": "Claim and Complete Make Work Observable",
"description": "claim_task records an owner and in_progress state; complete_task marks completion and reports downstream tasks that became unblocked.",
"alternatives": "A simple checklist can say done, but it cannot safely coordinate ownership or dependencies.",
"zh": {
"title": "Prompt Section 有明确归属",
"description": "identity、tools、workspace、memory 是分开的 section。这样更容易定位哪一层注入了错误指令。"
"title": "Claim 和 Complete 让工作可观察",
"description": "claim_task 记录 owner 和 in_progress 状态complete_task 标记完成,并报告被解锁的下游任务。"
},
"ja": {
"title": "prompt section には所有者がある",
"description": "identity、tools、workspace、memory を別 section にします。どのサブシステムが悪い指示を入れたかを追いやすくなります。"
"title": "claim と complete が作業を観測可能にする",
"description": "claim_task は owner と in_progress を記録し、complete_task は完了を記録して解放された下流タスクを報告します。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s11",
"decisions": [
{
"id": "classified-recovery",
"title": "Different Failures Need Different Recovery Paths",
"description": "max_tokens, prompt_too_long, and provider overload mean different things. The runtime classifies the error before retrying.",
"alternatives": "Blind retry is easy, but wastes time and can repeat a failure that needs compaction or token escalation.",
"id": "explicit-background-boundary",
"title": "Background Work Is an Execution Mode, Not a New Tool",
"description": "The lesson keeps the familiar tool surface and adds a background execution flag around slow operations. That makes the new mechanism visible: the same bash call can either block the loop or be moved to a thread. The agent learns that responsiveness is a runtime concern, not a reason to invent a separate tool for every slow task.",
"alternatives": "A dedicated background_bash tool would be simpler to route, but it would hide the more general idea that any slow operation can be scheduled asynchronously.",
"zh": {
"title": "不同失败需要不同恢复路径",
"description": "max_tokens、prompt_too_long 和供应商过载含义不同。运行时会先分类错误,再决定如何重试。"
"title": "后台任务是执行模式,而不是新工具",
"description": "课程保留原有工具表面,只在慢操作外增加后台执行标记。这样能清楚看到:同一个 bash 调用既可以阻塞主循环也可以放入线程。Agent 学到的是响应性属于运行时问题,而不是每个慢任务都要发明一个新工具。"
},
"ja": {
"title": "失敗ごとに異なる回復経路が必要",
"description": "max_tokens、prompt_too_long、プロバイダ過負荷は意味が違います。ランタイムは再試行前にエラーを分類します。"
"title": "バックグラウンド処理は新ツールではなく実行モード",
"description": "このレッスンでは既存のツール面を保ち、遅い操作にバックグラウンド実行フラグを加えます。同じ bash 呼び出しがループをブロックすることも、スレッドへ移すこともできる点が見えます。応答性はランタイムの責務であり、遅いタスクごとに新しいツールを作る必要はありません。"
}
},
{
"id": "recovery-state",
"title": "RecoveryState Prevents Infinite Retries",
"description": "The runtime tracks token escalation, compact retries, consecutive 529s, and fallback model use. Recovery becomes bounded and inspectable.",
"alternatives": "A while-retry loop can accidentally retry forever or hide which mitigation has already run.",
"id": "notification-reentry",
"title": "Completed Threads Re-enter as Notifications",
"description": "Background results are injected as task notifications instead of pretending to be immediate tool results. This preserves the chronology of the conversation: the model first sees that work started, and later sees that a task completed.",
"alternatives": "The thread could mutate the last tool result in place, but that would make the transcript impossible to reason about and hard to replay.",
"zh": {
"title": "RecoveryState 防止无限重试",
"description": "运行时记录 token 升级、compact retry、连续 529、fallback model 等状态。恢复因此有边界、可检查。"
"title": "线程完成后以通知形式回到循环",
"description": "后台结果会作为任务通知注入,而不是伪装成立即返回的 tool result。这样保留了对话时间线模型先看到任务已启动之后再看到任务完成。"
},
"ja": {
"title": "RecoveryState が無限リトライを防ぐ",
"description": "token 拡張、compact retry、連続 529、fallback model の利用を追跡します。回復処理に境界と可観測性を与えます。"
"title": "完了したスレッドは通知として戻る",
"description": "バックグラウンド結果は即時の tool result ではなくタスク通知として注入されます。モデルはまず作業開始を見て、その後に完了を知るため、会話の時系列が保たれます。"
}
},
{
"id": "no-fake-continuation",
"title": "Do Not Fake User Messages During Token Recovery",
"description": "The first max_tokens escalation retries without appending a synthetic user prompt. The transcript should reflect real events, not internal recovery tricks.",
"alternatives": "Always appending 'continue' is tempting, but it pollutes conversation history and may change model behavior.",
"id": "shared-result-store",
"title": "A Small Shared Store Keeps Threads Observable",
"description": "The implementation tracks background task state and results in explicit dictionaries. That keeps the code teachable while still exposing the hard parts of concurrency: ids, lifecycle state, and safe collection.",
"alternatives": "A full queue or job database adds durability, but it would obscure the minimal moving parts needed to understand threaded agent work.",
"zh": {
"title": "Token 恢复时不伪造用户消息",
"description": "第一次 max_tokens 升级会直接重试,不追加合成 user prompt。Transcript 应反映真实事件,而不是内部恢复技巧。"
"title": "小型共享存储让线程可观察",
"description": "实现用显式字典记录后台任务状态和结果。这样代码仍然易学,同时暴露并发中的关键问题:任务 id、生命周期状态和结果收集。"
},
"ja": {
"title": "トークン回復で偽のユーザーメッセージを入れない",
"description": "最初の max_tokens 拡張では合成 user prompt を追加せず再試行します。transcript は内部の回復処理ではなく実際の出来事を反映すべきです。"
"title": "小さな共有ストアでスレッドを観測可能にする",
"description": "実装は辞書でバックグラウンドタスクの状態と結果を追跡します。コードを学びやすく保ちながら、id、ライフサイクル、安全な収集という並行処理の要点を示します。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s12",
"decisions": [
{
"id": "tasks-as-files",
"title": "Tasks Are Durable JSON Files",
"description": "Each task is persisted under .tasks/ with id, subject, description, status, owner, and blockedBy. The task board survives context compaction and process restarts.",
"alternatives": "In-memory tasks are easier to code, but vanish exactly when long-running coordination needs them most.",
"id": "scheduler-outside-agent-loop",
"title": "The Scheduler Runs Outside the Agent Loop",
"description": "Cron matching is handled by a daemon loop rather than by asking the LLM to remember future times. This separates timekeeping from reasoning and makes recurring work reliable even when no user is actively chatting.",
"alternatives": "The agent could poll schedules inside each conversation turn, but missed turns would mean missed jobs.",
"zh": {
"title": "任务是持久 JSON 文件",
"description": "每个任务都持久化在 .tasks/ 下,包含 id、subject、description、status、owner、blockedBy。任务板能跨上下文压缩和进程重启保留。"
"title": "调度器运行在 Agent 循环之外",
"description": "Cron 匹配由独立守护循环处理,而不是让 LLM 记住未来时间。这把计时和推理分开,使定期任务在没有用户对话时也能可靠触发。"
},
"ja": {
"title": "タスクは永続 JSON ファイル",
"description": "各タスクは .tasks/ に id、subject、description、status、owner、blockedBy を持って保存されます。タスクボードはコンテキスト圧縮や再起動を越えて残ります。"
"title": "スケジューラはエージェントループの外で動く",
"description": "cron の照合は LLM に未来時刻を覚えさせるのではなく、デーモンループで処理します。時間管理と推論を分離し、ユーザーが会話していない時でも定期処理を確実にします。"
}
},
{
"id": "blockedby-dependencies",
"title": "blockedBy Encodes Ordering",
"description": "A task can only be claimed when all blockedBy dependencies are completed. Missing dependencies are treated as blocked to fail closed.",
"alternatives": "Letting the model remember ordering is fragile and hard for teammates to share.",
"id": "queue-decouples-time-from-work",
"title": "A Queue Decouples Due Time from Execution",
"description": "When a schedule matches, the scheduler enqueues work and lets a queue processor invoke the agent loop. That keeps cron matching fast and prevents long agent runs from blocking future schedule checks.",
"alternatives": "The scheduler could call the agent directly, but a slow job would stall the scheduler itself.",
"zh": {
"title": "blockedBy 编码任务顺序",
"description": "只有所有 blockedBy 依赖都完成时,任务才能被 claim。缺失依赖也被视为阻塞采用 fail closed。"
"title": "队列把到期判断和任务执行解耦",
"description": "当 schedule 匹配时,调度器只把任务放入队列,由队列处理器调用 agent_loop。这样 cron 匹配保持快速,长时间运行的 agent 任务不会阻塞后续调度检查。"
},
"ja": {
"title": "blockedBy が順序を表現する",
"description": "blockedBy の依存がすべて完了した時だけタスクを claim できます。存在しない依存もブロック扱いにして fail closed にします。"
"title": "キューが期限判定と実行を分離する",
"description": "スケジュールが一致すると、スケジューラは作業をキューへ入れ、キュープロセッサが agent_loop を呼び出します。cron 照合は速く保たれ、長いエージェント実行が次の確認を妨げません。"
}
},
{
"id": "claim-complete-lifecycle",
"title": "Claim and Complete Make Work Observable",
"description": "claim_task records an owner and in_progress state; complete_task marks completion and reports downstream tasks that became unblocked.",
"alternatives": "A simple checklist can say done, but it cannot safely coordinate ownership or dependencies.",
"id": "durable-schedules",
"title": "Schedules Are Durable Data",
"description": "Cron jobs are stored in a small JSON file so they survive process restarts. The lesson treats scheduled work as data that can be listed, cancelled, and inspected, not as hidden timers.",
"alternatives": "In-memory timers are shorter to implement, but they disappear on restart and are difficult to audit.",
"zh": {
"title": "Claim 和 Complete 让工作可观察",
"description": "claim_task 记录 owner 和 in_progress 状态complete_task 标记完成,并报告被解锁的下游任务。"
"title": "计划任务是持久数据",
"description": "Cron job 存储在小型 JSON 文件中,因此进程重启后仍然存在。课程把计划任务视为可列出、可取消、可检查的数据,而不是隐藏的计时器。"
},
"ja": {
"title": "claim と complete が作業を観測可能にする",
"description": "claim_task は owner と in_progress を記録し、complete_task は完了を記録して解放された下流タスクを報告します。"
"title": "スケジュールは永続データ",
"description": "cron ジョブは小さな JSON ファイルに保存され、プロセス再起動後も残ります。予定された作業を、一覧化、取り消し、検査できるデータとして扱います。"
}
}
]

View File

@@ -2,45 +2,101 @@
"version": "s13",
"decisions": [
{
"id": "explicit-background-boundary",
"title": "Background Work Is an Execution Mode, Not a New Tool",
"description": "The lesson keeps the familiar tool surface and adds a background execution flag around slow operations. That makes the new mechanism visible: the same bash call can either block the loop or be moved to a thread. The agent learns that responsiveness is a runtime concern, not a reason to invent a separate tool for every slow task.",
"alternatives": "A dedicated background_bash tool would be simpler to route, but it would hide the more general idea that any slow operation can be scheduled asynchronously.",
"id": "confirm-team-before-spawn",
"title": "The User Confirms the Team Before It Starts",
"description": "The Lead may notice that a request can be split, but it first proposes a small team with clear responsibilities. Teammates start only after the user confirms the extra agents.",
"alternatives": "Spawning immediately saves one turn, but hides the cost and coordination choice from the user.",
"zh": {
"title": "后台任务是执行模式,而不是新工具",
"description": "课程保留原有工具表面,只在慢操作外增加后台执行标记。这样能清楚看到:同一个 bash 调用既可以阻塞主循环也可以放入线程。Agent 学到的是响应性属于运行时问题,而不是每个慢任务都要发明一个新工具。"
"title": "启动团队前先征得用户确认",
"description": "Lead 可以判断一个需求适合拆分,但要先提出职责清晰的小团队。只有用户确认后,运行时才启动额外的 Agent。"
},
"ja": {
"title": "バックグラウンド処理は新ツールではなく実行モード",
"description": "このレッスンでは既存のツール面を保ち、遅い操作にバックグラウンド実行フラグを加えます。同じ bash 呼び出しがループをブロックすることも、スレッドへ移すこともできる点が見えます。応答性はランタイムの責務であり、遅いタスクごとに新しいツールを作る必要はありません。"
"title": "チームを起動する前にユーザーが確認する",
"description": "Lead は依頼を分割できると判断しても、まず役割が明確な小さなチームを提案する。追加 Agent はユーザーの確認後に起動する。"
}
},
{
"id": "notification-reentry",
"title": "Completed Threads Re-enter as Notifications",
"description": "Background results are injected as task notifications instead of pretending to be immediate tool results. This preserves the chronology of the conversation: the model first sees that work started, and later sees that a task completed.",
"alternatives": "The thread could mutate the last tool result in place, but that would make the transcript impossible to reason about and hard to replay.",
"id": "runtime-owned-delivery",
"title": "Message Delivery Belongs to the Runtime",
"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": "后台结果会作为任务通知注入,而不是伪装成立即返回的 tool result。这样保留了对话时间线模型先看到任务已启动之后再看到任务完成。"
"title": "消息投递由运行时负责",
"description": "MessageBus 持久化每次交接,运行时监听 Lead 邮箱,并把新的团队事件送入下一轮上下文。模型不需要浪费轮次轮询收件箱。"
},
"ja": {
"title": "完了したスレッドは通知として戻る",
"description": "バックグラウンド結果は即時の tool result ではなくタスク通知として注入されます。モデルはまず作業開始を見て、その後に完了を知るため、会話の時系列が保たれます。"
"title": "メッセージ配信はランタイムが担う",
"description": "MessageBus が各ハンドオフを永続化し、ランタイムが Lead の受信箱を監視して新しい team event を次の turn に注入する。モデルは受信箱のポーリングに turn を費やさない。"
}
},
{
"id": "shared-result-store",
"title": "A Small Shared Store Keeps Threads Observable",
"description": "The implementation tracks background task state and results in explicit dictionaries. That keeps the code teachable while still exposing the hard parts of concurrency: ids, lifecycle state, and safe collection.",
"alternatives": "A full queue or job database adds durability, but it would obscure the minimal moving parts needed to understand threaded agent work.",
"id": "typed-request-correlation",
"title": "Typed Requests Carry Correlation IDs",
"description": "Plan and shutdown requests use explicit message types and request ids. Replies can arrive in any order and still update the correct pending request.",
"alternatives": "Matching the latest free-form message works only until requests overlap.",
"zh": {
"title": "小型共享存储让线程可观察",
"description": "实现用显式字典记录后台任务状态和结果。这样代码仍然易学,同时暴露并发中的关键问题:任务 id、生命周期状态和结果收集。"
"title": "类型化请求携带关联 ID",
"description": "计划和关机请求使用明确的消息类型与 request id。即使回复顺序不同运行时也能更新正确的 pending request。"
},
"ja": {
"title": "小さな共有ストアでスレッドを観測可能にする",
"description": "実装は辞書でバックグラウンドタスクの状態と結果を追跡します。コードを学びやすく保ちながら、id、ライフサイクル、安全な収集という並行処理の要点を示します。"
"title": "型付きリクエストに対応 ID を持たせる",
"description": "プランと終了の要求は明示的な message type と request id を使う。返信順が変わっても、正しい pending request を更新できる。"
}
},
{
"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 rather than a conversational workaround.",
"alternatives": "Treating approval as a suggestion cannot prevent an early write or shell command.",
"zh": {
"title": "计划审批是执行闸门",
"description": "Lead 请求计划后,修改类工具会保持阻塞,直到对应计划通过。被拒绝的计划必须重新提交,不能靠对话绕过。"
},
"ja": {
"title": "プラン承認を実行ゲートにする",
"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 ルート配下の既知パスだけを受け付け、状態を確認できない場合や変更が残る場合は明示的な破棄なしに拒否し、タスクを副作用で完了させない。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s14",
"decisions": [
{
"id": "scheduler-outside-agent-loop",
"title": "The Scheduler Runs Outside the Agent Loop",
"description": "Cron matching is handled by a daemon loop rather than by asking the LLM to remember future times. This separates timekeeping from reasoning and makes recurring work reliable even when no user is actively chatting.",
"alternatives": "The agent could poll schedules inside each conversation turn, but missed turns would mean missed jobs.",
"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": "调度器运行在 Agent 循环之外",
"description": "Cron 匹配由独立守护循环处理,而不是让 LLM 记住未来时间。这把计时和推理分开,使定期任务在没有用户对话时也能可靠触发。"
"title": "MCP 工具使用规范化命名空间",
"description": "发现到的工具会暴露为 mcp__server__tool。前缀让工具来源明确也避免和内置工具或其他服务器工具冲突。"
},
"ja": {
"title": "スケジューラはエージェントループの外で動く",
"description": "cron の照合は LLM に未来時刻を覚えさせるのではなく、デーモンループで処理します。時間管理と推論を分離し、ユーザーが会話していない時でも定期処理を確実にします。"
"title": "MCP ツールは正規化された名前空間を使う",
"description": "発見されたツールは mcp__server__tool として公開されます。接頭辞により出所が明確になり、組み込みツールや別サーバーのツールとの衝突を避けます。"
}
},
{
"id": "queue-decouples-time-from-work",
"title": "A Queue Decouples Due Time from Execution",
"description": "When a schedule matches, the scheduler enqueues work and lets a queue processor invoke the agent loop. That keeps cron matching fast and prevents long agent runs from blocking future schedule checks.",
"alternatives": "The scheduler could call the agent directly, but a slow job would stall the scheduler itself.",
"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": "当 schedule 匹配时,调度器只把任务放入队列,由队列处理器调用 agent_loop。这样 cron 匹配保持快速,长时间运行的 agent 任务不会阻塞后续调度检查。"
"title": "工具发现会更新活动工具池",
"description": "连接服务器后,运行时会为下一次 LLM 调用组装新的工具池。模型只有在发现阶段让 MCP 工具可见之后,才能调用它们。"
},
"ja": {
"title": "キューが期限判定と実行を分離する",
"description": "スケジュールが一致すると、スケジューラは作業をキューへ入れ、キュープロセッサが agent_loop を呼び出します。cron 照合は速く保たれ、長いエージェント実行が次の確認を妨げません。"
"title": "ツール発見がアクティブなツールプールを更新する",
"description": "サーバー接続後、ランタイムは次の LLM 呼び出し用に新しいツールプールを組み立てます。MCP ツールは発見で可視化された後にのみモデルが利用できます。"
}
},
{
"id": "durable-schedules",
"title": "Schedules Are Durable Data",
"description": "Cron jobs are stored in a small JSON file so they survive process restarts. The lesson treats scheduled work as data that can be listed, cancelled, and inspected, not as hidden timers.",
"alternatives": "In-memory timers are shorter to implement, but they disappear on restart and are difficult to audit.",
"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": "Cron job 存储在小型 JSON 文件中,因此进程重启后仍然存在。课程把计划任务视为可列出、可取消、可检查的数据,而不是隐藏的计时器。"
"title": "外部结果复用 Tool Result 路径",
"description": "MCP 响应会像普通 tool result 一样追加到对话中。这样 agent 循环无需改变,同时外部系统仍然可以参与。"
},
"ja": {
"title": "スケジュールは永続データ",
"description": "cron ジョブは小さな JSON ファイルに保存され、プロセス再起動後も残ります。予定された作業を、一覧化、取り消し、検査できるデータとして扱います。"
"title": "外部結果は tool result 経路を再利用する",
"description": "MCP の応答は通常の tool result と同じように会話へ追加されます。エージェントループを変えずに外部システムを参加させられます。"
}
}
]

View File

@@ -2,101 +2,45 @@
"version": "s15",
"decisions": [
{
"id": "confirm-team-before-spawn",
"title": "The User Confirms the Team Before It Starts",
"description": "The Lead may notice that a request can be split, but it first proposes a small team with clear responsibilities. Teammates start only after the user confirms the extra agents.",
"alternatives": "Spawning immediately saves one turn, but hides the cost and coordination choice from the user.",
"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": "启动团队前先征得用户确认",
"description": "Lead 可以判断一个需求适合拆分,但要先提出职责清晰的小团队。只有用户确认后,运行时才启动额外的 Agent。"
"title": "Harness 组合既有层,而不是换掉循环",
"description": "集成后的 Harness 没有用新架构替换循环,而是把 memory、task、skill、后台任务、团队、worktree、MCP 组合到同一个模型-工具-结果循环周围。"
},
"ja": {
"title": "チームを起動する前にユーザーが確認する",
"description": "Lead は依頼を分割できると判断しても、まず役割が明確な小さなチームを提案する。追加 Agent はユーザーの確認後に起動する。"
"title": "Harness は既存レイヤーを統合する",
"description": "統合された Harness はループを新しい構造で置き換えません。memory、task、skill、バックグラウンド処理、チーム、worktree、MCP を同じ model-tool-result サイクルの周囲に合成します。"
}
},
{
"id": "runtime-owned-delivery",
"title": "Message Delivery Belongs to the Runtime",
"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.",
"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": "消息投递由运行时负责",
"description": "MessageBus 持久化每次交接,运行时监听 Lead 邮箱,并把新的团队事件送入下一轮上下文。模型不需要浪费轮次轮询收件箱。"
"title": "运行时状态来自具名来源",
"description": "上下文组装从 memory、task graph、skills、tool registry、policy 等具名来源读取。大型 agent 因此仍可调试,因为每块 prompt context 都有清晰归属。"
},
"ja": {
"title": "メッセージ配信はランタイムが担う",
"description": "MessageBus が各ハンドオフを永続化し、ランタイムが Lead の受信箱を監視して新しい team event を次の turn に注入する。モデルは受信箱のポーリングに turn を費やさない。"
"title": "ランタイム状態には名前付きの出所がある",
"description": "コンテキスト組み立ては memory、task graph、skills、tool registry、policy などの名前付きソースから取得します。各 prompt context に所有者があるため、大きなエージェントでもデバッグ可能です。"
}
},
{
"id": "typed-request-correlation",
"title": "Typed Requests Carry Correlation IDs",
"description": "Plan and shutdown requests use explicit message types and request ids. Replies can arrive in any order and still update the correct pending request.",
"alternatives": "Matching the latest free-form message works only until requests overlap.",
"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": "类型化请求携带关联 ID",
"description": "计划和关机请求使用明确的消息类型与 request id。即使回复顺序不同运行时也能更新正确的 pending request。"
"title": "恢复能力是一等流程",
"description": "压缩、错误恢复和异步结果收集都属于正常循环。Harness 通过明确的路径处理恢复与续跑,而不是把逻辑散落在异常分支中。"
},
"ja": {
"title": "型付きリクエストに対応 ID を持たせる",
"description": "プランと終了の要求は明示的な message type と request id を使う。返信順が変わっても、正しい pending request を更新できる。"
}
},
{
"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 rather than a conversational workaround.",
"alternatives": "Treating approval as a suggestion cannot prevent an early write or shell command.",
"zh": {
"title": "计划审批是执行闸门",
"description": "Lead 请求计划后,修改类工具会保持阻塞,直到对应计划通过。被拒绝的计划必须重新提交,不能靠对话绕过。"
},
"ja": {
"title": "プラン承認を実行ゲートにする",
"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 ルート配下の既知パスだけを受け付け、状態を確認できない場合や変更が残る場合は明示的な破棄なしに拒否し、タスクを副作用で完了させない。"
"title": "リカバリは主要フローの一部",
"description": "圧縮、エラー回復、非同期結果収集を通常のループ動作として扱います。Harness は回復と再開を名前付きの経路にまとめ、例外分岐へ散らしません。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s16",
"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 を失敗させ、項目を黙って落として成功扱いしない。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s17",
"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 を返します。"
}
}
]

View File

@@ -1,47 +0,0 @@
{
"version": "s18",
"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 を失敗させ、項目を黙って落として成功扱いしない。"
}
}
]
}

View File

@@ -1,47 +0,0 @@
{
"version": "s19",
"decisions": [
{
"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": "完成闸门由宿主持有",
"description": "工作模型可以请求停止,但 GoalController 会在 AgentSession 返回前评估 active goal。这个闸门就在原有的轮次边界上。"
},
"ja": {
"title": "完了ゲートはホストが所有する",
"description": "作業モデルは停止を要求できますが、GoalController は AgentSession が return する前に active goal を評価します。この gate は既存の turn 境界に置かれます。"
}
},
{
"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": "判断器接收 active condition 和当前对话,其中也包括已经写入的工具结果。它自己没有工具,只能根据对话中已有的内容判断。"
},
"ja": {
"title": "conversation が evaluator の入力になる",
"description": "evaluator は active condition と現在の conversation を受け取り、そこに記録された tool result も読みます。自身では tool を使えず、conversation にある内容だけで判断します。"
}
},
{
"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": "目标未满足时controller 把判断理由追加到 messages[]并在同一个循环里继续。Stop hook 的连续阻止上限或全局轮次上限会把控制权交还用户,同时让目标保持 active。"
},
"ja": {
"title": "上限では control を返し、goal は維持する",
"description": "goal が未達なら、controller は evaluator の理由を messages[] に追加し、同じ loop を続けます。Stop hook の連続 block 上限または global turn limit に達すると、goal を active のまま user に control を返します。"
}
}
]
}

View File

@@ -49,266 +49,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "append", to: "llm" },
],
},
s03: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "todo", label: "Create Todos", type: "process", x: COL_CENTER, y: 100 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 180 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 260 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT, y: 340 },
{ id: "append", label: "Append Result", type: "process", x: COL_LEFT, y: 410 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 340 },
],
edges: [
{ from: "start", to: "todo" },
{ from: "todo", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "exec", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s04: {
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 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "is_task", label: "task tool?", type: "decision", x: COL_LEFT, y: 280 },
{ id: "spawn", label: "Spawn Subagent\n(fresh messages[])", type: "subprocess", x: 60, y: 380 },
{ id: "sub_loop", label: "Subagent Loop", type: "process", x: 60, y: 460 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 380 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 540 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 280 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_task", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_task", to: "spawn", label: "task" },
{ from: "is_task", to: "exec", label: "other" },
{ from: "spawn", to: "sub_loop" },
{ from: "sub_loop", to: "append" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s05: {
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 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "is_skill", label: "load_skill?", type: "decision", x: COL_LEFT, y: 280 },
{ id: "load", label: "Read SKILL.md", type: "subprocess", x: 60, y: 370 },
{ id: "inject", label: "Inject via\ntool_result", type: "process", x: 60, y: 450 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 370 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 530 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 280 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_skill", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_skill", to: "load", label: "skill" },
{ from: "is_skill", to: "exec", label: "other" },
{ from: "load", to: "inject" },
{ from: "inject", to: "append" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s06: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "compress_check", label: "Over token\nlimit?", type: "decision", x: COL_CENTER, y: 110 },
{ id: "compress", label: "Compress Context", type: "subprocess", x: COL_RIGHT, y: 110 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 200 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 280 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT, y: 360 },
{ id: "append", label: "Append Result", type: "process", x: COL_LEFT, y: 430 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 360 },
],
edges: [
{ from: "start", to: "compress_check" },
{ from: "compress_check", to: "compress", label: "yes" },
{ from: "compress_check", to: "llm", label: "no" },
{ from: "compress", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "exec", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "exec", to: "append" },
{ from: "append", to: "compress_check" },
],
},
s07: {
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 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "is_task", label: "task_manager?", type: "decision", x: COL_LEFT, y: 280 },
{ id: "crud", label: "CRUD Task\n(file-based)", type: "subprocess", x: 60, y: 370 },
{ id: "dep_check", label: "Check\nDependencies", type: "process", x: 60, y: 450 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 370 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 530 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 280 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_task", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_task", to: "crud", label: "task" },
{ from: "is_task", to: "exec", label: "other" },
{ from: "crud", to: "dep_check" },
{ from: "dep_check", to: "append" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s08: {
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 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "bg_check", label: "Background?", type: "decision", x: COL_LEFT, y: 280 },
{ id: "bg_spawn", label: "Spawn Thread", type: "subprocess", x: 60, y: 370 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 370 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 450 },
{ id: "notify", label: "Notification\nQueue", type: "process", x: 60, y: 450 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 280 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "bg_check", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "bg_check", to: "bg_spawn", label: "bg" },
{ from: "bg_check", to: "exec", label: "fg" },
{ from: "bg_spawn", to: "notify" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
{ from: "notify", to: "llm" },
],
},
s09: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "llm", label: "LLM Call\n(team lead)", type: "process", x: COL_CENTER, y: 110 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 200 },
{ id: "is_team", label: "Team tool?", type: "decision", x: COL_LEFT, y: 290 },
{ id: "spawn", label: "Spawn\nTeammate", type: "subprocess", x: 60, y: 390 },
{ id: "msg", label: "Send Message\n(JSONL inbox)", type: "subprocess", x: 60, y: 470 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 390 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 550 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 290 },
{ id: "teammate", label: "Teammate Agent\n(own loop)", type: "process", x: COL_RIGHT, y: 470 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_team", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_team", to: "spawn", label: "spawn" },
{ from: "is_team", to: "exec", label: "other" },
{ from: "spawn", to: "teammate" },
{ from: "spawn", to: "msg" },
{ from: "msg", to: "append" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s10: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "llm", label: "LLM Call\n(team lead)", type: "process", x: COL_CENTER, y: 110 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 200 },
{ id: "is_proto", label: "Protocol?", type: "decision", x: COL_LEFT, y: 290 },
{ id: "shutdown", label: "Shutdown\nRequest", type: "subprocess", x: 60, y: 390 },
{ id: "fsm", label: "FSM:\npending->approved", type: "process", x: 60, y: 470 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 80, y: 390 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 550 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 290 },
{ id: "teammate", label: "Teammate\nreceives request_id", type: "process", x: COL_RIGHT, y: 470 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_proto", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_proto", to: "shutdown", label: "shutdown" },
{ from: "is_proto", to: "exec", label: "other" },
{ from: "shutdown", to: "fsm" },
{ from: "fsm", to: "teammate" },
{ from: "teammate", to: "append" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
],
},
s11: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "inbox", label: "Check Inbox", type: "process", x: COL_CENTER, y: 100 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 180 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 260 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT, y: 340 },
{ id: "append", label: "Append Result", type: "process", x: COL_LEFT, y: 410 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 340 },
{ id: "idle", label: "Idle Cycle", type: "process", x: COL_RIGHT, y: 420 },
{ id: "poll", label: "Poll Tasks\n+ Auto-Claim", type: "subprocess", x: COL_RIGHT, y: 500 },
],
edges: [
{ from: "start", to: "inbox" },
{ from: "inbox", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "exec", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "exec", to: "append" },
{ from: "append", to: "llm" },
{ from: "end", to: "idle" },
{ from: "idle", to: "poll" },
{ from: "poll", to: "inbox" },
],
},
s12: {
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 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "is_wt", label: "worktree tool?", type: "decision", x: COL_LEFT, y: 280 },
{ id: "task", label: "Task Board\\n(.tasks)", type: "process", x: 60, y: 360 },
{ id: "wt_create", label: "Allocate / Enter\\nWorktree", type: "subprocess", x: 60, y: 440 },
{ id: "wt_run", label: "Run in\\nIsolated Dir", type: "subprocess", x: COL_LEFT + 80, y: 360 },
{ id: "wt_close", label: "Closeout:\\nworktree_keep / remove", type: "process", x: COL_LEFT + 80, y: 440 },
{ id: "events", label: "Emit Lifecycle Events\\n(side-channel)", type: "process", x: COL_RIGHT, y: 420 },
{ id: "events_read", label: "Optional Read\\nworktree_events", type: "subprocess", x: COL_RIGHT, y: 520 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 530 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 280 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "is_wt", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "is_wt", to: "task", label: "task ops" },
{ from: "is_wt", to: "wt_create", label: "create/bind" },
{ from: "is_wt", to: "wt_run", label: "run/status" },
{ from: "task", to: "wt_create", label: "allocate lane" },
{ from: "wt_create", to: "wt_run" },
{ from: "task", to: "append", label: "task result" },
{ from: "wt_create", to: "events", label: "emit create" },
{ from: "wt_create", to: "append", label: "create result" },
{ from: "wt_run", to: "wt_close" },
{ from: "wt_run", to: "append", label: "run/status result" },
{ from: "wt_close", to: "events", label: "emit closeout" },
{ from: "wt_close", to: "append", label: "closeout result" },
{ from: "events", to: "events_read", label: "optional query" },
{ from: "events_read", to: "append", label: "events result" },
{ from: "append", to: "llm" },
],
},
s13: {
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 },
@@ -338,7 +79,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "append", to: "llm" },
],
},
s14: {
s12: {
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 },
@@ -366,7 +107,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "agent", to: "llm" },
],
},
s15: {
s13: {
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 },
@@ -401,7 +142,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "bus", to: "lead", label: "runtime delivery" },
],
},
s16: {
s14: {
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 },
@@ -428,7 +169,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "append", to: "llm" },
],
},
s17: {
s15: {
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 },
@@ -462,7 +203,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "recover", to: "context" },
],
},
s18: {
s16: {
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 },
@@ -486,7 +227,7 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ from: "output", to: "notify" },
],
},
s19: {
s17: {
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 },
@@ -697,58 +438,6 @@ const CURRENT_FLOW_OVERRIDES: Record<string, FlowDefinition> = {
],
},
s10: {
nodes: [
{ id: "start", label: "Runtime State", type: "start", x: COL_CENTER, y: 30 },
{ id: "sections", label: "PROMPT_SECTIONS", type: "process", x: COL_CENTER, y: 120 },
{ id: "context", label: "Build Context\nmemory/tools/workspace", type: "process", x: COL_CENTER, y: 220 },
{ id: "cache", label: "Cache Hit?", type: "decision", x: COL_CENTER, y: 320 },
{ id: "reuse", label: "Reuse Prompt", type: "process", x: COL_RIGHT, y: 420 },
{ id: "assemble", label: "Assemble Prompt", type: "subprocess", x: COL_LEFT, y: 420 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 540 },
{ id: "loop", label: "Tool Loop", type: "subprocess", x: COL_CENTER, y: 640 },
],
edges: [
{ from: "start", to: "sections" },
{ from: "sections", to: "context" },
{ from: "context", to: "cache" },
{ from: "cache", to: "reuse", label: "yes" },
{ from: "cache", to: "assemble", label: "no" },
{ from: "reuse", to: "llm" },
{ from: "assemble", to: "llm" },
{ from: "llm", to: "loop" },
{ from: "loop", to: "context" },
],
},
s11: {
nodes: [
{ id: "start", label: "LLM Request", type: "start", x: COL_CENTER, y: 30 },
{ id: "try", label: "try LLM Call", type: "process", x: COL_CENTER, y: 120 },
{ id: "ok", label: "success?", type: "decision", x: COL_CENTER, y: 220 },
{ id: "tools", label: "Execute Tools", type: "process", x: COL_RIGHT, y: 330 },
{ id: "classify", label: "Classify Error", type: "decision", x: COL_LEFT, y: 330 },
{ id: "tokens", label: "max_tokens\nEscalate", type: "subprocess", x: 40, y: 440 },
{ id: "prompt", label: "prompt_too_long\nCompact", type: "subprocess", x: COL_LEFT, y: 610 },
{ id: "backoff", label: "429 / 529\nBackoff", type: "subprocess", x: COL_LEFT + 140, y: 440 },
{ id: "fallback", label: "Fallback Model", type: "process", x: COL_RIGHT, y: 540 },
{ id: "retry", label: "Retry Request", type: "process", x: COL_CENTER, y: 740 },
],
edges: [
{ from: "start", to: "try" },
{ from: "try", to: "ok" },
{ from: "ok", to: "tools", label: "yes" },
{ from: "ok", to: "classify", label: "error" },
{ from: "classify", to: "tokens", label: "max_tokens" },
{ from: "classify", to: "prompt", label: "too long" },
{ from: "classify", to: "backoff", label: "429/529" },
{ from: "backoff", to: "fallback", label: "repeated 529" },
{ from: "tokens", to: "retry" },
{ from: "prompt", to: "retry" },
{ from: "backoff", to: "retry" },
{ from: "fallback", to: "retry" },
{ from: "retry", to: "try" },
],
},
s12: {
nodes: [
{ id: "start", label: "User Goal", type: "start", x: COL_CENTER, y: 30 },
{ id: "create", label: "create_task", type: "subprocess", x: COL_CENTER, y: 120 },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,27 +1,48 @@
{
"version": "s10",
"title": "Context Assembly",
"description": "Stable instructions and dynamic runtime state are assembled at the model boundary and cached by a deterministic context key.",
"title": "Task System",
"description": "A file-persisted task graph tracks status, ownership, and blockedBy dependencies.",
"steps": [
{
"type": "system_event",
"content": "context={workspace:\"/repo\", memories:true, skills:[\"code-review\"], tools:[\"bash\",\"read_file\"]}",
"annotation": "Prompt inputs are explicit runtime data."
"type": "user_message",
"content": "Break the release into tasks and block deployment until tests pass.",
"annotation": "The user asks for durable multi-step coordination."
},
{
"type": "system_event",
"content": "cache miss -> assemble_system_prompt(context)",
"annotation": "A new context key causes sections to be selected and joined."
"type": "tool_call",
"toolName": "create_task",
"content": "{\"subject\":\"Run tests\"}",
"annotation": "Tasks are created as JSON files under .tasks/."
},
{
"type": "assistant_text",
"content": "I can use the workspace, memory index, tool guidance, and available skills for this task.",
"annotation": "The LLM sees the assembled prompt, not a hardcoded static string."
"type": "tool_call",
"toolName": "create_task",
"content": "{\"subject\":\"Deploy release\",\"blockedBy\":[\"task_tests\"]}",
"annotation": "blockedBy encodes dependency ordering."
},
{
"type": "system_event",
"content": "same context key -> reuse cached prompt",
"annotation": "Repeated turns avoid rebuilding identical prompt text."
"type": "tool_call",
"toolName": "claim_task",
"content": "{\"task_id\":\"task_deploy\",\"owner\":\"agent\"}",
"annotation": "The claim fails until dependencies are complete."
},
{
"type": "tool_result",
"toolName": "claim_task",
"content": "Blocked by: [\"task_tests\"]",
"annotation": "The task graph prevents premature work."
},
{
"type": "tool_call",
"toolName": "complete_task",
"content": "{\"task_id\":\"task_tests\"}",
"annotation": "Completing a dependency can unblock downstream tasks."
},
{
"type": "tool_result",
"toolName": "complete_task",
"content": "Completed task_tests\nUnblocked: Deploy release",
"annotation": "The harness reports newly available work."
}
]
}

View File

@@ -1,33 +1,45 @@
{
"version": "s11",
"title": "Error Recovery",
"description": "LLM calls are wrapped with targeted recovery paths for token limits, prompt overflow, and transient provider errors.",
"title": "Background Tasks",
"description": "Long-running work moves to a thread while the agent keeps the main loop responsive.",
"steps": [
{
"type": "system_event",
"content": "try LLM call with max_tokens=8000",
"annotation": "The normal path is still a regular model call."
"type": "user_message",
"content": "Run a long repository scan and tell me when it finishes.",
"annotation": "The user asks for work that may exceed the normal foreground loop."
},
{
"type": "system_event",
"content": "error=max_tokens -> escalate to 64000 without appending a fake user message",
"annotation": "Token exhaustion gets a continuation-aware recovery path."
},
{
"type": "system_event",
"content": "error=prompt_too_long -> reactive_compact(messages) -> retry once",
"annotation": "Prompt overflow triggers compaction instead of blind retry."
},
{
"type": "system_event",
"content": "error=529 -> exponential backoff with jitter; repeated 529 -> fallback model",
"annotation": "Transient provider errors use backoff and model fallback."
"type": "assistant_text",
"content": "I'll start the scan in the background so we can keep the conversation responsive.",
"annotation": "The model chooses the background path instead of blocking on the tool result."
},
{
"type": "tool_call",
"toolName": "read_file",
"content": "{\"path\":\"README.md\"}",
"annotation": "After recovery succeeds, the agent continues the normal tool loop."
"toolName": "bash",
"content": "rg \"TODO|FIXME\" .",
"toolInput": "{\"background\":true}",
"annotation": "The tool call is wrapped with a background flag."
},
{
"type": "system_event",
"content": "background_tasks[task_42] = Thread(target=run_bash)",
"annotation": "The runtime starts a thread and returns immediately."
},
{
"type": "tool_result",
"toolName": "bash",
"content": "Started background task task_42.",
"annotation": "A placeholder result is appended to the main conversation."
},
{
"type": "system_event",
"content": "<task_notification id=\"task_42\" status=\"done\">Found 12 matches.</task_notification>",
"annotation": "The completed thread injects a notification back into the next loop."
},
{
"type": "assistant_text",
"content": "The scan finished with 12 matches. I'll summarize the most important ones now.",
"annotation": "The agent consumes the background result as fresh context."
}
]
}

View File

@@ -1,48 +1,44 @@
{
"version": "s12",
"title": "Task System",
"description": "A file-persisted task graph tracks status, ownership, and blockedBy dependencies.",
"title": "Cron Scheduler",
"description": "A daemon loop watches durable cron definitions, queues due jobs, and lets the agent process them.",
"steps": [
{
"type": "user_message",
"content": "Break the release into tasks and block deployment until tests pass.",
"annotation": "The user asks for durable multi-step coordination."
"content": "Every weekday morning, remind me to review open tasks.",
"annotation": "The user asks for a recurring future action."
},
{
"type": "assistant_text",
"content": "I'll create a cron job and store it so the scheduler can pick it up later.",
"annotation": "The agent delegates recurrence to the scheduler instead of relying on current context."
},
{
"type": "tool_call",
"toolName": "create_task",
"content": "{\"subject\":\"Run tests\"}",
"annotation": "Tasks are created as JSON files under .tasks/."
},
{
"type": "tool_call",
"toolName": "create_task",
"content": "{\"subject\":\"Deploy release\",\"blockedBy\":[\"task_tests\"]}",
"annotation": "blockedBy encodes dependency ordering."
},
{
"type": "tool_call",
"toolName": "claim_task",
"content": "{\"task_id\":\"task_deploy\",\"owner\":\"agent\"}",
"annotation": "The claim fails until dependencies are complete."
"toolName": "schedule_cron",
"content": "{\"name\":\"review-open-tasks\",\"cron\":\"0 9 * * 1-5\",\"prompt\":\"Review open tasks and summarize blockers.\"}",
"annotation": "The new tool validates and persists a cron definition."
},
{
"type": "tool_result",
"toolName": "claim_task",
"content": "Blocked by: [\"task_tests\"]",
"annotation": "The task graph prevents premature work."
"toolName": "schedule_cron",
"content": "Scheduled review-open-tasks with id cron_7.",
"annotation": "The job is written to the durable schedule store."
},
{
"type": "tool_call",
"toolName": "complete_task",
"content": "{\"task_id\":\"task_tests\"}",
"annotation": "Completing a dependency can unblock downstream tasks."
"type": "system_event",
"content": "cron_scheduler_loop: cron_7 matched current minute -> cron_queue.put(cron_7)",
"annotation": "The independent scheduler daemon detects that the job is due."
},
{
"type": "tool_result",
"toolName": "complete_task",
"content": "Completed task_tests\nUnblocked: Deploy release",
"annotation": "The harness reports newly available work."
"type": "system_event",
"content": "queue_processor_loop: dequeued cron_7 and invoked agent_loop(prompt)",
"annotation": "A separate processor turns due jobs into normal agent work."
},
{
"type": "assistant_text",
"content": "Scheduled. When the cron fires, the queue processor will run the reminder prompt through the agent loop.",
"annotation": "The final answer explains the durable recurring behavior."
}
]
}

View File

@@ -1,45 +1,122 @@
{
"version": "s13",
"title": "Background Tasks",
"description": "Long-running work moves to a thread while the agent keeps the main loop responsive.",
"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": "Run a long repository scan and tell me when it finishes.",
"annotation": "The user asks for work that may exceed the normal foreground loop."
"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": "I'll start the scan in the background so we can keep the conversation responsive.",
"annotation": "The model chooses the background path instead of blocking on the tool result."
"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": "The team starts only after explicit confirmation."
},
{
"type": "tool_call",
"toolName": "bash",
"content": "rg \"TODO|FIXME\" .",
"toolInput": "{\"background\":true}",
"annotation": "The tool call is wrapped with a background flag."
},
{
"type": "system_event",
"content": "background_tasks[task_42] = Thread(target=run_bash)",
"annotation": "The runtime starts a thread and returns immediately."
"toolName": "create_task",
"content": "{\"subject\":\"Refactor authentication\"}",
"annotation": "The shared task board remains the source of ownership and status."
},
{
"type": "tool_result",
"toolName": "bash",
"content": "Started background task task_42.",
"annotation": "A placeholder result is appended to the main conversation."
"toolName": "create_task",
"content": "Created task_1a2b3c4d: 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_1a2b3c4d\"}",
"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_1a2b3c4d\"]}",
"annotation": "The task graph keeps dependent work from starting early."
},
{
"type": "tool_result",
"toolName": "create_task",
"content": "Created task_5e6f7a8b: Update authentication tests (blockedBy: task_1a2b3c4d)",
"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\":\"Review the assigned authentication task and propose a plan.\",\"task_id\":\"task_1a2b3c4d\",\"require_plan\":true}",
"annotation": "The runtime claims the initial task and activates the plan gate before the teammate thread starts."
},
{
"type": "tool_call",
"toolName": "spawn_teammate",
"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": "<task_notification id=\"task_42\" status=\"done\">Found 12 matches.</task_notification>",
"annotation": "The completed thread injects a notification back into the next loop."
"content": "spawn_teammate(backend, task_1a2b3c4d) -> task_store_lock commits owner=backend before thread start",
"annotation": "The ownership check and persisted state transition share the cross-process task-store lock."
},
{
"type": "tool_call",
"toolName": "request_plan",
"content": "{\"teammate\":\"backend\",\"task\":\"Inspect the claimed authentication task and submit a plan before changing files.\"}",
"annotation": "The Lead delivers the plan request for the current assignment; the gate was already active before the teammate thread started."
},
{
"type": "system_event",
"content": "backend submit_plan -> plan_approval_request(request_id=req_000007, task_id=task_1a2b3c4d)",
"annotation": "The request records the task and work version that the plan is meant to authorize."
},
{
"type": "tool_call",
"toolName": "review_plan",
"content": "{\"request_id\":\"req_000007\",\"approve\":true,\"feedback\":\"Proceed with the scoped refactor.\"}",
"annotation": "Approval is correlated by request ID and cannot carry into a different assignment."
},
{
"type": "system_event",
"content": "plan_approval_response(request_id=req_000007, approve=true) -> backend",
"annotation": "The teammate receives the typed response before mutating tools are released."
},
{
"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_1a2b3c4d\"}",
"annotation": "Completing the first task makes its dependent test task ready."
},
{
"type": "system_event",
"content": "backend -> Lead: result(auth refactor complete) -> idle_notification",
"annotation": "The task directory stays selected through the completion turn, then IDLE releases the assignment."
},
{
"type": "system_event",
"content": "claim_next_task(tests) -> task_5e6f7a8b; task_store_lock commits owner=tests",
"annotation": "An idle teammate discovers newly ready work without another direct assignment."
},
{
"type": "system_event",
"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": "The scan finished with 12 matches. I'll summarize the most important ones now.",
"annotation": "The agent consumes the background result as fresh context."
"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."
}
]
}

View File

@@ -1,44 +1,46 @@
{
"version": "s14",
"title": "Cron Scheduler",
"description": "A daemon loop watches durable cron definitions, queues due jobs, and lets the agent process them.",
"title": "MCP Tools",
"description": "The agent discovers external MCP tools and exposes them through a normalized tool namespace.",
"steps": [
{
"type": "user_message",
"content": "Every weekday morning, remind me to review open tasks.",
"annotation": "The user asks for a recurring future action."
},
{
"type": "assistant_text",
"content": "I'll create a cron job and store it so the scheduler can pick it up later.",
"annotation": "The agent delegates recurrence to the scheduler instead of relying on current context."
"content": "Search the documentation for deployment guidance.",
"annotation": "The user asks for a tool source outside the built-in set."
},
{
"type": "tool_call",
"toolName": "schedule_cron",
"content": "{\"name\":\"review-open-tasks\",\"cron\":\"0 9 * * 1-5\",\"prompt\":\"Review open tasks and summarize blockers.\"}",
"annotation": "The new tool validates and persists a cron definition."
"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": "schedule_cron",
"content": "Scheduled review-open-tasks with id cron_7.",
"annotation": "The job is written to the durable schedule store."
"toolName": "connect_mcp",
"content": "Connected to MCP server 'docs'. Discovered 2 tools: search, get_version",
"annotation": "Tool discovery expands the active tool pool."
},
{
"type": "system_event",
"content": "cron_scheduler_loop: cron_7 matched current minute -> cron_queue.put(cron_7)",
"annotation": "The independent scheduler daemon detects that the job is due."
"type": "tool_call",
"toolName": "mcp__docs__search",
"content": "{\"query\":\"deployment\"}",
"annotation": "The LLM can now call the discovered tool by its normalized name."
},
{
"type": "system_event",
"content": "queue_processor_loop: dequeued cron_7 and invoked agent_loop(prompt)",
"annotation": "A separate processor turns due jobs into normal agent work."
"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": "assistant_text",
"content": "Scheduled. When the cron fires, the queue processor will run the reminder prompt through the agent loop.",
"annotation": "The final answer explains the durable recurring behavior."
"content": "The docs server found three matches for deployment guidance.",
"annotation": "The agent summarizes external tool output for the user."
}
]
}

View File

@@ -1,122 +1,102 @@
{
"version": "s15",
"title": "Agent Team Runtime",
"description": "The Lead coordinates persistent teammates while the runtime owns message delivery, atomic task claims, and task-bound worktree directories.",
"title": "Integrated Harness",
"description": "The harness combines system instructions, tools, memory, teams, background work, cron, worktrees, and MCP.",
"steps": [
{
"type": "user_message",
"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."
"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 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."
"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, go ahead.",
"annotation": "The team starts only after explicit confirmation."
"content": "Yes. Start the docs teammate and continue.",
"annotation": "User confirmation opens the team boundary inherited from S13."
},
{
"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\":\"Refactor authentication\"}",
"annotation": "The shared task board remains the source of ownership and status."
"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: Refactor authentication",
"annotation": "The runtime-generated task ID is carried into every later operation on this 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": "{\"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."
"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_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."
"toolName": "create_worktree",
"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": "spawn_teammate",
"content": "{\"name\":\"backend\",\"role\":\"backend engineer\",\"prompt\":\"Claim the authentication task and propose a plan.\",\"require_plan\":true}",
"annotation": "The plan gate is active before the teammate thread starts, so claim and mutation cannot race ahead of approval."
"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": "spawn_teammate",
"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."
"toolName": "bash",
"content": "{\"command\":\"python -m unittest tests.test_agent_teams_runtime\",\"run_in_background\":true}",
"annotation": "Long-running validation goes through the background task path."
},
{
"type": "system_event",
"content": "claim_next_task(backend) -> task_1712345678_0042; task_store_lock commits owner=backend",
"annotation": "The ownership check and persisted state transition share the cross-process task-store lock."
"content": "permission: user approved the exact test command",
"annotation": "Team confirmation does not authorize shell execution; the foreground turn asks separately before dispatch."
},
{
"type": "tool_call",
"toolName": "request_plan",
"content": "{\"teammate\":\"backend\",\"task\":\"Inspect the claimed authentication task and submit a plan before changing files.\"}",
"annotation": "The Lead delivers the plan request for the current assignment; the gate was already active before the teammate thread started."
},
{
"type": "system_event",
"content": "backend submit_plan -> plan_approval_request(request_id=req_000007, task_id=task_1712345678_0042)",
"annotation": "The request records the task and work version that the plan is meant to authorize."
"toolName": "connect_mcp",
"content": "{\"name\":\"deploy\"}",
"annotation": "External capabilities are added only when needed."
},
{
"type": "tool_call",
"toolName": "review_plan",
"content": "{\"request_id\":\"req_000007\",\"approve\":true,\"feedback\":\"Proceed with the scoped refactor.\"}",
"annotation": "Approval is correlated by request ID and cannot carry into a different assignment."
"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": "plan_approval_response(request_id=req_000007, approve=true) -> backend",
"annotation": "The teammate receives the typed response before mutating tools are released."
},
{
"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": "The task directory stays selected through the completion turn, then IDLE releases the assignment."
},
{
"type": "system_event",
"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": "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."
"content": "task_notification(status=completed): tests passed; teammate result and deploy status appended",
"annotation": "The integrated runtime folds asynchronous results back into the loop."
},
{
"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."
"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."
}
]
}

View File

@@ -1,46 +1,54 @@
{
"version": "s16",
"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 load_user 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": "{\"name\":\"docs\"}",
"annotation": "The runtime creates an MCP client for the named server."
"toolName": "Workflow",
"content": "{\"name\":\"review-changes\",\"args\":{\"budget\":null,\"changes\":\"def load_user(user_id):\\n query = f\\\"SELECT * FROM users WHERE id = {user_id}\\\"\\n return db.execute(query).fetchone()\\n\"}}",
"annotation": "The model selects a saved workflow and arguments; the host registry supplies its trusted metadata and script."
},
{
"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_0000000000001a7b) -> 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_0000000000001a7b.output.json)",
"annotation": "The task emits its final lifecycle event after output is written."
},
{
"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."
"toolName": "Workflow",
"content": "{\"launched\":{\"status\":\"async_launched\",\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\"},\"result\":{\"confirmed\":[{\"dimension\":\"performance\",\"title\":\"audit:performance #1\",\"severity\":\"medium\"},{\"dimension\":\"performance\",\"title\":\"audit:performance #2\",\"severity\":\"medium\"},{\"dimension\":\"style\",\"title\":\"audit:style #1\",\"severity\":\"medium\"},{\"dimension\":\"style\",\"title\":\"audit:style #2\",\"severity\":\"medium\"},{\"dimension\":\"security\",\"title\":\"audit:security #1\",\"severity\":\"low\"},{\"dimension\":\"security\",\"title\":\"audit:security #2\",\"severity\":\"low\"}]},\"task\":{\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\",\"status\":\"completed\",\"usage\":{\"agents\":11,\"tokens\":883},\"progress\":[{\"type\":\"workflow_phase\",\"title\":\"Review\"},{\"type\":\"workflow_agent\",\"label\":\"audit:correctness\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_phase\",\"title\":\"Verify\"},{\"type\":\"workflow_agent\",\"label\":\"audit:security\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:performance\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:style\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:correctness:audit:correctness #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_log\",\"message\":\"confirmed 6 real finding(s)\"}]}}",
"annotation": "The demo fixture returns six synthetic findings and measured runner usage; these are not claims about the repository."
},
{
"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": "[docs] Found 3 results for 'deployment'",
"annotation": "The external result is appended like any other tool result."
},
{
"type": "assistant_text",
"content": "The docs server found three matches for deployment guidance.",
"annotation": "The agent summarizes external tool output for the user."
"type": "system_event",
"content": "append Workflow tool_result -> messages[]",
"annotation": "A main-loop integration can append this JSON-safe result and continue with the updated conversation."
}
]
}

View File

@@ -1,102 +1,49 @@
{
"version": "s17",
"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 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."
"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": "user_message",
"content": "Yes. Start the docs teammate and continue.",
"annotation": "User confirmation opens the team boundary inherited from S15."
"type": "system_event",
"content": "goal_evaluated ok=false -> block",
"annotation": "The evaluator finds no test exit code in the conversation."
},
{
"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": "{\"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": "Worktree 'release-docs' created for task_1712345678_0042",
"annotation": "The task now carries the checkout used by its eventual owner."
},
{
"type": "tool_call",
"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": "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": "{\"command\":\"python -m unittest tests.test_agent_teams_runtime\",\"run_in_background\":true}",
"annotation": "Long-running validation goes through the background task path."
},
{
"type": "system_event",
"content": "permission: user approved the exact test command",
"annotation": "Team confirmation does not authorize shell execution; the foreground turn asks separately before dispatch."
},
{
"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."
"content": "pytest tests/auth",
"annotation": "The next agent turn runs the missing 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": "task_notification(status=completed): tests passed; teammate result and deploy status appended",
"annotation": "The integrated runtime folds asynchronous results back into the loop."
"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."
}
]
}

View File

@@ -1,54 +0,0 @@
{
"version": "s18",
"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": "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\",\"args\":{\"budget\":null}}",
"annotation": "The model selects a saved workflow and arguments; the host registry supplies its trusted metadata and script."
},
{
"type": "system_event",
"content": "async_launched(runId=wf_review-changes_0000000000001a7b) -> 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_0000000000001a7b.output.json)",
"annotation": "The task emits its final lifecycle event after output is written."
},
{
"type": "tool_result",
"toolName": "Workflow",
"content": "{\"launched\":{\"status\":\"async_launched\",\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\"},\"result\":{\"confirmed\":[{\"dimension\":\"security\",\"title\":\"audit:security #1\",\"severity\":\"high\"},{\"dimension\":\"style\",\"title\":\"audit:style #1\",\"severity\":\"high\"},{\"dimension\":\"security\",\"title\":\"audit:security #2\",\"severity\":\"medium\"},{\"dimension\":\"performance\",\"title\":\"audit:performance #2\",\"severity\":\"medium\"},{\"dimension\":\"correctness\",\"title\":\"audit:correctness #1\",\"severity\":\"low\"},{\"dimension\":\"performance\",\"title\":\"audit:performance #1\",\"severity\":\"low\"}]},\"task\":{\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\",\"status\":\"completed\",\"usage\":{\"agents\":11,\"tokens\":352},\"progress\":[{\"type\":\"workflow_phase\",\"title\":\"Review\"},{\"type\":\"workflow_agent\",\"label\":\"audit:correctness\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_phase\",\"title\":\"Verify\"},{\"type\":\"workflow_agent\",\"label\":\"audit:security\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:performance\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:style\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:correctness:audit:correctness #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_log\",\"message\":\"confirmed 6 real finding(s)\"}]}}",
"annotation": "The deterministic sample returns its six fixture findings and measured runner usage; these are not claims about the repository."
},
{
"type": "system_event",
"content": "append Workflow tool_result -> messages[]",
"annotation": "A main-loop integration can append this JSON-safe result and continue with the updated conversation."
}
]
}

View File

@@ -1,49 +0,0 @@
{
"version": "s19",
"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": "/goal pytest tests/auth exits with code 0",
"annotation": "The command stores an active completion condition and starts the work."
},
{
"type": "assistant_text",
"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": "system_event",
"content": "goal_evaluated ok=false -> block",
"annotation": "The evaluator finds no test exit code in the conversation."
},
{
"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": "pytest tests/auth",
"annotation": "The next agent turn runs the missing check."
},
{
"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": "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."
}
]
}

View File

@@ -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": "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" },
"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": "17 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 s19: Progressive Agent Harness Design", "layer_legend": "Layer Legend", "loc_growth": "LOC Growth", "learn_more": "Learn More" },
"timeline": { "title": "Learning Path", "subtitle": "s01 to s17: 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",
@@ -48,16 +48,14 @@
"s07": "Skills",
"s08": "Context Compact",
"s09": "Memory",
"s10": "Context Assembly",
"s11": "Error Recovery",
"s12": "Task System",
"s13": "Background Tasks",
"s14": "Cron Scheduler",
"s15": "Agent Team Runtime",
"s16": "MCP Tools",
"s17": "Integrated Harness",
"s18": "Workflow Runtime",
"s19": "Goal Loop"
"s10": "Task System",
"s11": "Background Tasks",
"s12": "Cron Scheduler",
"s13": "Agent Team Runtime",
"s14": "MCP Tools",
"s15": "Integrated Harness",
"s16": "Workflow Runtime",
"s17": "Goal Loop"
},
"layer_labels": {
"tools": "Tools & Execution",
@@ -76,15 +74,13 @@
"s07": "On-Demand Skill Loading",
"s08": "Three-Layer Context Compression",
"s09": "Memory Library",
"s10": "Runtime Context Assembly",
"s11": "Error Recovery Paths",
"s12": "Task Board Dependencies",
"s13": "Background Task Lanes",
"s14": "Cron Scheduler",
"s15": "Team Runtime: Message, Claim, Bind",
"s16": "MCP Tool Bridge",
"s17": "Integrated Harness Turn",
"s18": "Workflow Runtime",
"s19": "Goal Completion Gate"
"s10": "Task Board Dependencies",
"s11": "Background Task Lanes",
"s12": "Cron Scheduler",
"s13": "Team Runtime: Message, Claim, Bind",
"s14": "MCP Tool Bridge",
"s15": "Integrated Harness Turn",
"s16": "Workflow Runtime",
"s17": "Goal Completion Gate"
}
}

View File

@@ -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": "19の段階的セッション、シンプルなループから決定的な編成と目標完了まで", "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": "17の段階的セッション、シンプルなループから決定的な編成と目標完了まで", "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からs19へ:段階的エージェント Harness 設計", "layer_legend": "レイヤー凡例", "loc_growth": "コード量の推移", "learn_more": "詳細を見る" },
"timeline": { "title": "学習パス", "subtitle": "s01からs17へ:段階的エージェント Harness 設計", "layer_legend": "レイヤー凡例", "loc_growth": "コード量の推移", "learn_more": "詳細を見る" },
"layers": {
"title": "アーキテクチャ層",
"subtitle": "5つの直交する関心事が完全なエージェントを構成",
@@ -48,16 +48,14 @@
"s07": "スキル",
"s08": "コンテキスト圧縮",
"s09": "メモリ",
"s10": "コンテキスト組み立て",
"s11": "エラー回復",
"s12": "タスクシステム",
"s13": "バックグラウンドタスク",
"s14": "Cron スケジューラー",
"s15": "Agent Team Runtime",
"s16": "MCP ツール",
"s17": "Integrated Harness",
"s18": "Workflow Runtime",
"s19": "Goal Loop"
"s10": "タスクシステム",
"s11": "バックグラウンドタスク",
"s12": "Cron スケジューラー",
"s13": "Agent Team Runtime",
"s14": "MCP ツール",
"s15": "Integrated Harness",
"s16": "Workflow Runtime",
"s17": "Goal Loop"
},
"layer_labels": {
"tools": "ツールと実行",
@@ -76,15 +74,13 @@
"s07": "オンデマンド スキルローディング",
"s08": "3層コンテキスト圧縮",
"s09": "メモリライブラリ",
"s10": "実行時コンテキスト組み立て",
"s11": "エラー回復経路",
"s12": "タスクボード依存関係",
"s13": "バックグラウンドタスクレーン",
"s14": "Cron スケジューラー",
"s15": "Team Runtimeメッセージ・認領・ディレクトリ紐付け",
"s16": "MCP ツールブリッジ",
"s17": "Integrated Harness のターン",
"s18": "Workflow Runtime",
"s19": "目標完了ゲート"
"s10": "タスクボード依存関係",
"s11": "バックグラウンドタスクレーン",
"s12": "Cron スケジューラー",
"s13": "Team Runtimeメッセージ・認領・ディレクトリ紐付け",
"s14": "MCP ツールブリッジ",
"s15": "Integrated Harness のターン",
"s16": "Workflow Runtime",
"s17": "目標完了ゲート"
}
}

View File

@@ -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": "19 个渐进式课程,从简单循环到确定性编排与目标闭环", "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": "17 个渐进式课程,从简单循环到确定性编排与目标闭环", "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 到 s19:渐进式 Agent Harness 设计", "layer_legend": "层次图例", "loc_growth": "代码量增长", "learn_more": "了解更多" },
"timeline": { "title": "学习路径", "subtitle": "s01 到 s17:渐进式 Agent Harness 设计", "layer_legend": "层次图例", "loc_growth": "代码量增长", "learn_more": "了解更多" },
"layers": {
"title": "架构层次",
"subtitle": "五个正交关注点组合成完整的 Agent",
@@ -48,16 +48,14 @@
"s07": "Skills",
"s08": "Context Compact",
"s09": "Memory",
"s10": "Context Assembly",
"s11": "Error Recovery",
"s12": "Task System",
"s13": "Background Tasks",
"s14": "Cron Scheduler",
"s15": "Agent Team Runtime",
"s16": "MCP Tools",
"s17": "Agent Harness 集成",
"s18": "Workflow Runtime",
"s19": "Goal Loop"
"s10": "Task System",
"s11": "Background Tasks",
"s12": "Cron Scheduler",
"s13": "Agent Team Runtime",
"s14": "MCP Tools",
"s15": "Agent Harness 集成",
"s16": "Workflow Runtime",
"s17": "Goal Loop"
},
"layer_labels": {
"tools": "工具与执行",
@@ -76,15 +74,13 @@
"s07": "On-Demand Skill Loading",
"s08": "Three-Layer Context Compact",
"s09": "记忆图书馆",
"s10": "运行时上下文组装",
"s11": "Error Recovery Paths",
"s12": "任务看板依赖",
"s13": "Background Task Lanes",
"s14": "Cron Scheduler",
"s15": "团队运行时:消息、认领与目录绑定",
"s16": "MCP Tool Bridge",
"s17": "Agent Harness 集成流程",
"s18": "Workflow Runtime",
"s19": "目标完成闸门"
"s10": "任务看板依赖",
"s11": "Background Task Lanes",
"s12": "Cron Scheduler",
"s13": "团队运行时:消息、认领与目录绑定",
"s14": "MCP Tool Bridge",
"s15": "Agent Harness 集成流程",
"s16": "Workflow Runtime",
"s17": "目标完成闸门"
}
}

View File

@@ -18,8 +18,6 @@ export const VERSION_ORDER = [
"s15",
"s16",
"s17",
"s18",
"s19",
] as const;
export const LEARNING_PATH = VERSION_ORDER;
@@ -107,84 +105,68 @@ export const VERSION_META: Record<string, {
prevVersion: "s08",
},
s10: {
title: "Context Assembly",
subtitle: "Build Model Input from Runtime State",
coreAddition: "Runtime context assembly",
keyInsight: "Stable instructions and dynamic state should be assembled deliberately at the model boundary.",
layer: "planning",
prevVersion: "s09",
},
s11: {
title: "Error Recovery",
subtitle: "Errors Are the Start of a Retry",
coreAddition: "Retry strategy",
keyInsight: "A robust harness classifies failures and decides what kind of retry is worthwhile.",
layer: "planning",
prevVersion: "s10",
},
s12: {
title: "Task System",
subtitle: "Break Big Goals into Small Tasks",
coreAddition: "Task board",
keyInsight: "A task graph turns vague goals into ordered, observable work.",
layer: "collaboration",
prevVersion: "s11",
prevVersion: "s09",
},
s13: {
s11: {
title: "Background Tasks",
subtitle: "Slow Operations Go to the Background",
coreAddition: "Background execution",
keyInsight: "The agent can keep reasoning while slow work completes elsewhere.",
layer: "concurrency",
prevVersion: "s12",
prevVersion: "s10",
},
s14: {
s12: {
title: "Cron Scheduler",
subtitle: "Producing Work on a Schedule",
coreAddition: "Scheduled task creation",
keyInsight: "Recurring work should be created by the harness, not remembered by the model.",
layer: "concurrency",
prevVersion: "s13",
prevVersion: "s11",
},
s15: {
s13: {
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",
prevVersion: "s12",
},
s16: {
s14: {
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: "s15",
prevVersion: "s13",
},
s17: {
s15: {
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: "s16",
prevVersion: "s14",
},
s18: {
s16: {
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: "s17",
prevVersion: "s15",
},
s19: {
s17: {
title: "Goal Loop",
subtitle: "Independent Evaluation Decides When to Stop",
coreAddition: "Goal completion gate",
keyInsight: "A durable goal keeps the loop working until an independent evaluator finds the completion condition satisfied in the conversation.",
layer: "planning",
prevVersion: "s18",
prevVersion: "s16",
},
};
@@ -199,7 +181,7 @@ export const LAYERS = [
id: "planning" as const,
label: "Planning & Control",
color: "#10B981",
versions: ["s05", "s06", "s07", "s10", "s11", "s19"],
versions: ["s05", "s06", "s07", "s17"],
},
{
id: "memory" as const,
@@ -211,12 +193,12 @@ export const LAYERS = [
id: "concurrency" as const,
label: "Concurrency & Scheduling",
color: "#F59E0B",
versions: ["s13", "s14", "s18"],
versions: ["s11", "s12", "s16"],
},
{
id: "collaboration" as const,
label: "Multi-Agent Platform",
color: "#EF4444",
versions: ["s12", "s15", "s16", "s17"],
versions: ["s10", "s13", "s14", "s15"],
},
] as const;