feat: complete web curriculum visuals

This commit is contained in:
Haoran
2026-05-25 22:37:07 +08:00
parent 956d8272ce
commit fdd7d2a851
155 changed files with 19086 additions and 3584 deletions

View File

@@ -2,45 +2,45 @@
"version": "s03",
"decisions": [
{
"id": "visible-planning",
"title": "Making Plans Visible via TodoWrite",
"description": "Instead of letting the model plan silently in its chain-of-thought, we force plans to be externalized through the TodoWrite tool. Each plan item has a status (pending, in_progress, completed) that gets tracked explicitly. This has three benefits: (1) users can see what the agent intends to do before it does it, (2) developers can debug agent behavior by inspecting the plan state, (3) the agent itself can refer back to its plan in later turns when earlier context has scrolled away.",
"alternatives": "The model could plan internally via chain-of-thought reasoning (as it does in v0/v1). Internal planning works but is invisible and ephemeral -- once the thinking scrolls out of context, the plan is lost. Claude's extended thinking is another option, but it's not inspectable by the user or by downstream tools.",
"id": "permission-before-execution",
"title": "Permission Runs Before Tool Execution",
"description": "The permission check is inserted between the model's tool call and the handler. The model can ask for an action, but the harness decides whether the action may touch the real workspace.",
"alternatives": "Putting permission inside each tool duplicates policy and makes it easier for a new tool to forget the check.",
"zh": {
"title": "通过 TodoWrite 让计划可见",
"description": "我们不让模型在思维链中默默规划,而是强制通过 TodoWrite 工具将计划外化。每个计划项都有可追踪的状态pending、in_progress、completed。这有三个好处(1) 用户可以在执行前看到 agent 打算做什么;(2) 开发者可以通过检查计划状态来调试 agent 行为;(3) agent 自身可以在后续轮次中引用计划,即使早期上下文已经滚出窗口。"
"title": "权限检查发生在工具执行前",
"description": "权限检查插在模型 tool call 和 handler 之间。模型可以请求动作,但是否允许触碰真实工作区由 harness 决定。"
},
"ja": {
"title": "TodoWrite による計画の可視化",
"description": "モデルが思考の連鎖の中で黙って計画するのではなく、TodoWrite ツールを通じて計画を外部化することを強制します。各計画項目には追跡可能なステータスpending、in_progress、completedがあります。利点は3つ(1) ユーザーがエージェントの意図を実行前に確認できる、(2) 開発者が計画状態を検査してデバッグできる、(3) エージェント自身が以前のコンテキストがスクロールアウトした後でも計画を参照できる。"
"title": "権限確認はツール実行前に行う",
"description": "権限確認はモデルの tool call と handler の間に入ります。モデルは操作を要求できますが、実際のワークスペースに触れてよいかは harness が決めます。"
}
},
{
"id": "single-in-progress",
"title": "Only One Task Can Be In-Progress",
"description": "The TodoWrite tool enforces that at most one task has status 'in_progress' at any time. If the model tries to start a second task, it must first complete or abandon the current one. This constraint prevents a subtle failure mode: models that try to 'multitask' by interleaving work on multiple items tend to lose track of state and produce half-finished results. Sequential focus produces higher quality than parallel thrashing.",
"alternatives": "Allowing multiple in-progress items would let the agent context-switch between tasks, which seems more flexible. In practice, LLMs handle context-switching poorly -- they lose track of which task they were working on and mix up details between tasks. The single-focus constraint is a guardrail that improves output quality.",
"id": "three-gate-model",
"title": "Three Gates Keep Policy Explainable",
"description": "Hard denies, rule matches, and user approval are separate gates. That makes it clear whether a command was impossible, risky, or simply waiting for confirmation.",
"alternatives": "A single allow/deny function is shorter, but it hides why a command stopped.",
"zh": {
"title": "同一时间只允许一个任务进行中",
"description": "TodoWrite 工具强制要求任何时候最多只能有一个任务处于 in_progress 状态。如果模型想开始第二个任务,必须先完成或放弃当前任务。这个约束防止了一种隐蔽的失败模式:试图通过交替处理多个项目来'多任务'的模型,往往会丢失状态并产出半成品。顺序执行的专注度远高于并行切换。"
"title": "三道门让策略可解释",
"description": "硬拒绝、规则匹配、用户确认是分开的关卡。这样能看清命令是绝对禁止、有风险,还是只是在等确认。"
},
"ja": {
"title": "同時に進行中にできるタスクは1つだけ",
"description": "TodoWrite ツールは、同時に 'in_progress' 状態のタスクを最大1つに制限します。モデルが2つ目のタスクを開始しようとする場合、まず現在のタスクを完了または中断する必要があります。この制約は微妙な失敗モードを防ぎます複数の項目を交互に処理して「マルチタスク」しようとするモデルは、状態を見失い中途半端な結果を生みがちです。逐次的な集中は並行的な切り替えよりも高品質な出力を生み出します。"
"title": "三つのゲートでポリシーを説明可能にする",
"description": "ハード拒否、ルール照合、ユーザー承認を分けます。コマンドが不可能なのか、危険なのか、確認待ちなのかが明確になります。"
}
},
{
"id": "max-twenty-items",
"title": "Maximum of 20 Plan Items",
"description": "TodoWrite caps the plan at 20 items. This is a deliberate constraint against over-planning. Models tend to decompose tasks into increasingly fine-grained steps when unconstrained, producing 50-item plans where each step is trivial. Long plans are fragile: if step 15 fails, the remaining 35 steps may all be invalid. Short plans (under 20 items) stay at the right abstraction level and are easier to adapt when reality diverges from the plan.",
"alternatives": "No cap would give the model full flexibility, but in practice leads to absurdly detailed plans. A dynamic cap (proportional to task complexity) would be smarter but adds complexity. The fixed cap of 20 is a simple heuristic that works well empirically -- most real coding tasks can be expressed in 5-15 meaningful steps.",
"id": "continue-with-tool-result",
"title": "Blocked Calls Still Produce Loop State",
"description": "A blocked tool call must still leave the loop in a coherent state. The user and model need to know why execution did not happen before choosing the next action.",
"alternatives": "Silently skipping a blocked tool is simpler, but the model may repeat the same unsafe request.",
"zh": {
"title": "计划项上限为 20 条",
"description": "TodoWrite 将计划项限制在 20 条以内。这是对过度规划的刻意约束。不加限制时,模型倾向于将任务分解成越来越细粒度的步骤,产出 50 条的计划,每一步都微不足道。冗长的计划很脆弱:如果第 15 步失败,剩下的 35 步可能全部作废。20 条以内的短计划保持在正确的抽象层级,更容易在现实偏离计划时做出调整。"
"title": "被拦截的调用也要留下循环状态",
"description": "被拦截的工具调用仍然要让循环状态保持一致。用户和模型都需要知道为什么没有执行,才能决定下一步。"
},
"ja": {
"title": "計画項目の上限は20個",
"description": "TodoWrite は計画を20項目に制限します。これは過度な計画に対する意図的な制約です。制約がないとモデルはタスクをどんどん細かいステップに分解し、各ステップが些末な50項目の計画を作りがちです。長い計画は脆弱ですステップ15が失敗すると残りの35ステップは全て無効になりかねません。20項目以内の短い計画は適切な抽象度を保ち、現実が計画から逸脱した際の適応が容易です。"
"title": "ブロックされた呼び出しもループ状態を残す",
"description": "ブロックされたツール呼び出しでも、ループの状態は一貫している必要があります。実行されなかった理由が分かって初めて次の行動を選べます。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s04",
"decisions": [
{
"id": "context-isolation",
"title": "Subagents Get Fresh Context, Not Shared History",
"description": "When a parent agent spawns a subagent via the Task tool, the subagent starts with a clean message history containing only the system prompt and the delegated task description. It does NOT inherit the parent's conversation. This is context isolation: the subagent can focus entirely on its specific subtask without being distracted by hundreds of messages from the parent's broader conversation. The result is returned to the parent as a single tool_result, collapsing potentially dozens of subagent turns into one concise answer.",
"alternatives": "Sharing the parent's full context would give the subagent more information, but it would also flood the subagent with irrelevant details. Context window is finite -- filling it with parent history leaves less room for the subagent's own work. Fork-based approaches (copy the parent context) are a middle ground but still waste tokens on irrelevant history.",
"id": "hooks-around-loop",
"title": "Hooks Wrap the Loop Instead of Rewriting It",
"description": "UserPromptSubmit, PreToolUse, PostToolUse, and Stop hooks attach behavior at lifecycle points while the model-tool-result loop stays unchanged.",
"alternatives": "Inlining logging and permission into the loop is quicker at first, but every new cross-cutting concern would make the loop harder to read.",
"zh": {
"title": "子代理获得全新上下文,而非共享历史",
"description": "当父代理通过 Task 工具创建子代理时,子代理从全新的消息历史开始,只包含系统提示词和委派的任务描述,不继承父代理的对话。这就是上下文隔离:子代理可以完全专注于特定子任务,不会被父代理长达数百条消息的对话干扰。结果作为单条 tool_result 返回给父代理,将子代理可能数十轮的交互压缩为一个简洁的回答。"
"title": "Hook 包裹循环,而不是改写循环",
"description": "UserPromptSubmit、PreToolUse、PostToolUse、Stop 等 hook 挂在生命周期节点上,模型-工具-结果循环本身保持不变。"
},
"ja": {
"title": "サブエージェントは共有履歴ではなく新しいコンテキストを取得",
"description": "親エージェントが Task ツールでサブエージェントを生成すると、サブエージェントはシステムプロンプトと委任されたタスク説明のみを含むクリーンなメッセージ履歴から開始します。親の会話は引き継ぎません。これがコンテキスト分離です:サブエージェントは親の広範な会話の何百ものメッセージに気を取られることなく、特定のサブタスクに完全に集中できます。結果は単一の tool_result として親に返され、サブエージェントの数十ターンが1つの簡潔な回答に凝縮されます。"
"title": "フックはループを書き換えず包み込む",
"description": "UserPromptSubmit、PreToolUse、PostToolUse、Stop の各フックはライフサイクル地点に処理を追加し、model-tool-result ループ自体は保ちます。"
}
},
{
"id": "tool-filtering",
"title": "Explore Agents Cannot Write Files",
"description": "When spawning a subagent with the 'Explore' type, it receives only read-only tools: bash (with restrictions), read_file, and search tools. It cannot call write_file or edit_file. This implements the principle of least privilege: an agent tasked with 'find all usages of function X' doesn't need write access. Removing write tools eliminates the risk of accidental file modification during exploration, and it also narrows the tool space so the model makes better decisions with fewer options.",
"alternatives": "Giving all subagents full tool access is simpler to implement but violates least privilege. A permission-request system (subagent asks parent for write access) adds complexity and latency. Static tool filtering by role is the pragmatic middle ground -- simple to implement, effective at preventing accidents.",
"id": "pretooluse-can-block",
"title": "PreToolUse Is the Policy Boundary",
"description": "PreToolUse hooks run after the model chooses a tool but before the handler executes. That is the natural point for permission, logging, and safety policy.",
"alternatives": "Checking after execution can only report damage; checking before execution can prevent it.",
"zh": {
"title": "Explore 代理不能写入文件",
"description": "创建 Explore 类型的子代理时它只获得只读工具bash有限制、read_file 和搜索工具,不能调用 write_file 或 edit_file。这实现了最小权限原则一个被委派'查找函数 X 所有使用位置'的代理不需要写权限。移除写工具消除了探索过程中误修改文件的风险,同时缩小了工具空间,让模型在更少的选项中做出更好的决策。"
"title": "PreToolUse 是策略边界",
"description": "PreToolUse 在模型选择工具之后、handler 执行之前运行。这是权限、日志和安全策略最自然的插入点。"
},
"ja": {
"title": "Explore エージェントはファイルを書き込めない",
"description": "Explore タイプのサブエージェントを生成すると、読み取り専用ツールのみが提供されますbash制限付き、read_file、検索ツール。write_file や edit_file は使えません。これは最小権限の原則の実装です:「関数 X の全使用箇所を見つける」タスクに書き込み権限は不要です。書き込みツールを除外することで探索中の誤ったファイル変更リスクを排除し、ツール空間を狭めてモデルがより良い判断を下せるようにします。"
"title": "PreToolUse はポリシー境界",
"description": "PreToolUse はモデルがツールを選んだ後、handler 実行前に動きます。権限、ログ、安全ポリシーを入れる自然な場所です。"
}
},
{
"id": "no-recursive-task",
"title": "Subagents Cannot Spawn Their Own Subagents",
"description": "The Task tool is not included in the subagent's tool set. A subagent must complete its work directly -- it cannot delegate further. This prevents infinite delegation loops: without this constraint, an agent could spawn a subagent that spawns another subagent, each one re-delegating the same task in slightly different words, consuming tokens without making progress. One level of delegation handles the vast majority of use cases. If a task is too complex for a single subagent, the parent should decompose it differently.",
"alternatives": "Allowing recursive delegation (bounded by depth) would handle deeply nested tasks but adds complexity and the risk of runaway token consumption. In practice, single-level delegation covers most real-world coding tasks. Multi-level delegation is addressed in later versions (v6+) through persistent team structures instead of recursive spawning.",
"id": "posttooluse-observability",
"title": "PostToolUse Keeps Results Observable",
"description": "PostToolUse hooks can inspect output size, record traces, or transform metadata without changing every tool handler.",
"alternatives": "Each tool could log its own result, but cross-tool behavior would drift.",
"zh": {
"title": "子代理不能再创建子代理",
"description": "Task 工具不包含在子代理的工具集中。子代理必须直接完成工作,不能继续委派。这防止了无限委派循环:没有这个约束,一个代理可能创建子代理,子代理又创建子代理,每一层都用略微不同的措辞重新委派同一任务,消耗 token 却毫无进展。一层委派足以处理绝大多数场景。如果任务对单个子代理来说太复杂,应该由父代理重新分解。"
"title": "PostToolUse 让结果可观察",
"description": "PostToolUse 可以检查输出大小、记录 trace 或转换元数据,而不需要修改每个工具 handler。"
},
"ja": {
"title": "サブエージェントは自身のサブエージェントを生成できない",
"description": "Task ツールはサブエージェントのツールセットに含まれません。サブエージェントは作業を直接完了しなければならず、さらなる委任はできません。これにより無限委任ループを防止します:この制約がなければ、エージェントがサブエージェントを生成し、そのサブエージェントがさらにサブエージェントを生成し、それぞれが微妙に異なる言葉で同じタスクを再委任してトークンを消費するだけで進捗しない可能性があります。一段階の委任で大多数のユースケースに対応できます。"
"title": "PostToolUse が結果を観測可能にする",
"description": "PostToolUse は出力サイズ確認、trace 記録、メタデータ変換を各 handler へ散らさずに行えます。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s05",
"decisions": [
{
"id": "tool-result-injection",
"title": "Skills Inject via tool_result, Not System Prompt",
"description": "When the agent invokes the Skill tool, the skill's content (a SKILL.md file) is returned as a tool_result in a user message, not injected into the system prompt. This is a deliberate caching optimization: the system prompt remains static across turns, which means API providers can cache it (Anthropic's prompt caching, OpenAI's system message caching). If skill content were in the system prompt, it would change every time a new skill is loaded, invalidating the cache. By putting dynamic content in tool_result, we keep the expensive system prompt cacheable while still getting skill knowledge into context.",
"alternatives": "Injecting skills into the system prompt is simpler and gives skills higher priority in the model's attention. But it breaks prompt caching (every skill load creates a new system prompt variant) and bloats the system prompt over time as skills accumulate. The tool_result approach keeps things cache-friendly at the cost of slightly lower attention priority.",
"id": "todo-as-tool",
"title": "TodoWrite Is a Tool, Not a Hidden Planner",
"description": "Planning enters the same tool dispatch path as bash or read_file. This keeps the planning state visible and lets the model decide when to update it.",
"alternatives": "An automatic planner could create todos behind the scenes, but the user would not see when the model's intent changed.",
"zh": {
"title": "Skill 通过 tool_result 注入,而非系统提示词",
"description": "当 agent 调用 Skill 工具时Skill 内容SKILL.md 文件)作为 tool_result 在用户消息中返回而非注入系统提示词。这是一个刻意的缓存优化系统提示词在各轮次间保持静态API 提供商可以缓存它Anthropic 的 prompt caching、OpenAI 的 system message caching。如果 Skill 内容在系统提示词中,每次加载新 Skill 都会使缓存失效。将动态内容放在 tool_result 中,既保持了昂贵的系统提示词可缓存,又让 Skill 知识进入了上下文。"
"title": "TodoWrite 是工具,不是隐藏规划器",
"description": "规划通过和 bash、read_file 相同的工具分发路径进入系统。这样计划状态可见,模型也能决定何时更新。"
},
"ja": {
"title": "スキルはシステムプロンプトではなく tool_result で注入",
"description": "エージェントが Skill ツールを呼び出すと、スキルの内容SKILL.md ファイル)はシステムプロンプトへの注入ではなく、ユーザーメッセージ内の tool_result として返されます。これは意図的なキャッシュ最適化ですシステムプロンプトはターン間で静的に保たれるため、API プロバイダーがキャッシュできますAnthropic のプロンプトキャッシュ、OpenAI のシステムメッセージキャッシュ)。スキル内容がシステムプロンプト内にあると、新しいスキルをロードするたびにキャッシュが無効化されます。動的コンテンツを tool_result に配置することで、高コストなシステムプロンプトのキャッシュ可能性を維持しつつ、スキル知識をコンテキストに取り込めます。"
"title": "TodoWrite は隠れたプランナーではなくツール",
"description": "計画は bash や read_file と同じツールディスパッチ経路に入ります。計画状態が可視化され、モデルが更新タイミングを選べます。"
}
},
{
"id": "lazy-loading",
"title": "On-Demand Skill Loading Instead of Upfront",
"description": "Skills are not loaded at startup. The agent starts with only the skill names and descriptions (from frontmatter). When the agent decides it needs a specific skill, it calls the Skill tool, which loads the full SKILL.md body into context. This keeps the initial prompt small and focused. An agent solving a Python bug doesn't need the Kubernetes deployment skill loaded -- that would waste context window space and potentially confuse the model with irrelevant instructions.",
"alternatives": "Loading all skills upfront guarantees the model always has all knowledge available, but wastes tokens on irrelevant skills and may hit context limits. A recommendation system (model suggests skills, human approves) adds latency. Lazy loading lets the model self-serve the knowledge it needs, when it needs it.",
"id": "nag-reminder",
"title": "A Lightweight Reminder Prevents Plan Drift",
"description": "After several rounds without todo updates, the runtime injects a reminder. The reminder nudges the model without taking over the plan.",
"alternatives": "Forcing a todo update every turn would be noisy and slow down simple tasks.",
"zh": {
"title": "按需加载 Skill 而非预加载",
"description": "Skill 不会在启动时加载。Agent 初始只拥有 Skill 名称和描述(来自 frontmatter。当 agent 判断需要特定 Skill 时,调用 Skill 工具将完整的 SKILL.md 内容加载到上下文中。这保持了初始提示词的精简。一个正在修复 Python bug 的 agent 不需要加载 Kubernetes 部署 Skill——那会浪费上下文窗口空间还可能用无关指令干扰模型。"
"title": "轻量提醒防止计划漂移",
"description": "多个回合没有更新 todo 后,运行时会注入提醒。它只提醒模型,不接管计划。"
},
"ja": {
"title": "起動時ではなくオンデマンドでスキルを読み込み",
"description": "スキルは起動時に読み込まれません。エージェントは最初、スキルの名前と説明フロントマターからのみを持ちます。エージェントが特定のスキルが必要だと判断すると、Skill ツールを呼び出して完全な SKILL.md の内容をコンテキストに読み込みます。これにより初期プロンプトを小さく保ちます。Python のバグを修正しているエージェントに Kubernetes デプロイのスキルは不要です――コンテキストウィンドウの無駄遣いであり、無関係な指示でモデルを混乱させかねません。"
"title": "軽量リマインダーで計画の漂流を防ぐ",
"description": "数ターン todo 更新がない場合、ランタイムはリマインダーを注入します。計画を奪わず、モデルに更新を促します。"
}
},
{
"id": "frontmatter-body-split",
"title": "YAML Frontmatter + Markdown Body in SKILL.md",
"description": "Each SKILL.md file has two parts: YAML frontmatter (name, description, globs) and a markdown body (the actual instructions). The frontmatter serves as metadata for the skill registry -- it's what gets listed when the agent asks 'what skills are available?' The body is the payload that gets loaded on demand. This separation means you can list 100 skills (reading only frontmatter, a few bytes each) without loading 100 full instruction sets (potentially thousands of tokens each).",
"alternatives": "A separate metadata file (skill.yaml + skill.md) would work but doubles the number of files. Embedding metadata in the markdown (as headings or comments) requires parsing the full file to extract metadata. Frontmatter is a well-established convention (Jekyll, Hugo, Astro) that keeps metadata and content co-located but separately parseable.",
"id": "in-memory-current-work",
"title": "Current Todos Stay Session-Local",
"description": "TodoWrite is for the current session's working plan, not durable project management. Later task systems handle persistent work.",
"alternatives": "Persisting every todo immediately would blur the difference between a scratch plan and a durable task graph.",
"zh": {
"title": "SKILL.md 采用 YAML Frontmatter + Markdown 正文",
"description": "每个 SKILL.md 文件有两部分YAML frontmatter名称、描述、globs和 markdown 正文实际指令。Frontmatter 作为 Skill 注册表的元数据——当 agent 问'有哪些可用 Skill'时,展示的就是这些信息。正文是按需加载的有效负载。这种分离意味着可以列出 100 个 Skill每个只读几字节的 frontmatter而不必加载 100 套完整指令集(每套可能数千 token。"
"title": "当前 Todo 保持会话内状态",
"description": "TodoWrite 面向当前会话的工作计划,不是持久项目管理。后面的任务系统负责持久工作。"
},
"ja": {
"title": "SKILL.md で YAML フロントマター + Markdown 本文",
"description": "各 SKILL.md ファイルは2つの部分で構成されますYAML フロントマター名前、説明、globsと Markdown 本文実際の指示。フロントマターはスキルレジストリのメタデータとして機能し、エージェントが「どんなスキルが利用可能か」と問い合わせた際に一覧表示されます。本文はオンデマンドで読み込まれるペイロードです。この分離により、100個のスキル一覧表示各数バイトのフロントマターのみ読み取りが100個の完全な指示セット各数千トークンのロードなしに可能になります。"
"title": "現在の todo はセッション内に留める",
"description": "TodoWrite は現在セッションの作業計画であり、永続的なプロジェクト管理ではありません。永続作業は後のタスクシステムが扱います。"
}
}
]

View File

@@ -2,59 +2,45 @@
"version": "s06",
"decisions": [
{
"id": "three-layer-compression",
"title": "Three-Layer Compression Strategy",
"description": "Context management uses three distinct layers, each with different cost/benefit profiles. (1) Microcompact runs every turn and is nearly free: it truncates tool_result blocks from older messages, stripping verbose command output that's no longer needed. (2) Auto_compact triggers when token count exceeds a threshold: it calls the LLM to generate a conversation summary, which is expensive but dramatically reduces context size. (3) Manual compact is user-triggered for explicit 'start fresh' moments. Layering these means the cheap operation runs constantly (keeping context tidy) while the expensive operation runs rarely (only when actually needed).",
"alternatives": "A single compression strategy (e.g., always summarize at 80% capacity) would be simpler but wasteful -- most of the time, microcompact alone keeps things manageable. A sliding window (drop oldest N messages) is cheap but loses important context. The three-layer approach gives the best token efficiency: cheap cleanup constantly, expensive summarization rarely.",
"id": "fresh-subagent-context",
"title": "Subagents Start with Fresh Messages",
"description": "The child agent receives only the delegated prompt. This isolates exploratory work and prevents the parent context from filling with every intermediate tool result.",
"alternatives": "Sharing the full parent history gives more context, but it defeats the purpose of delegation as context isolation.",
"zh": {
"title": "三层压缩策略",
"description": "上下文管理使用三个独立的层次,各有不同的成本收益比。(1) 微压缩每轮都运行,几乎零成本:它截断旧消息中的 tool_result 块,去除不再需要的冗长命令输出。(2) 自动压缩在 token 数超过阈值时触发:调用 LLM 生成对话摘要,代价高但能大幅缩减上下文。(3) 手动压缩由用户触发,用于明确的'重新开始'场景。分层意味着低成本操作持续运行(保持上下文整洁),而高成本操作很少触发(仅在真正需要时)。"
"title": "子代理从全新 Messages 开始",
"description": "子代理只收到被委派的 prompt。这样探索性工作被隔离父上下文不会塞满每个中间工具结果。"
},
"ja": {
"title": "3層圧縮戦略",
"description": "コンテキスト管理は、異なるコスト・効果プロファイルを持つ3つの層を使用します。(1) マイクロコンパクトは毎ターン実行されほぼ無コスト:古いメッセージの tool_result ブロックを切り詰め、不要な冗長出力を除去します。(2) 自動コンパクトはトークン数が閾値を超えると発動LLM を呼び出して会話の要約を生成し、コストは高いがコンテキストサイズを劇的に削減します。(3) 手動コンパクトはユーザーが明示的に「最初からやり直し」する時に使用します。この階層化により、安価な操作が常に実行され(コンテキストを整頓)、高価な操作はめったに実行されません(本当に必要な時のみ)。"
"title": "サブエージェントは新しい messages で始まる",
"description": "子エージェントは委任された prompt だけを受け取ります。探索作業を隔離し、親コンテキストが中間 tool result で膨らむのを防ぎます。"
}
},
{
"id": "min-savings-threshold",
"title": "MIN_SAVINGS = 20,000 Tokens Before Compressing",
"description": "Auto_compact only triggers when the estimated savings (current tokens minus estimated summary size) exceed 20,000 tokens. Compression is not free: the summary itself consumes tokens, plus there's the API call cost to generate it. If the conversation is only 25,000 tokens, compressing might save 5,000 tokens but cost an API call and produce a summary that's less coherent than the original. The 20K threshold ensures compression only happens when the savings meaningfully exceed the overhead.",
"alternatives": "A percentage-based threshold (compress when context is 80% full) adapts to different context window sizes but doesn't account for the fixed cost of generating a summary. A fixed threshold of 10K would compress more aggressively but often isn't worth it. The 20K value was chosen empirically: it's the point where compression savings consistently outweigh the quality loss from summarization.",
"id": "summary-only-return",
"title": "Only the Summary Returns to the Parent",
"description": "The parent receives a compact final answer, not the child's full transcript. That gives delegation a predictable context cost.",
"alternatives": "Returning the full transcript can help debugging, but it makes large subagent runs expensive to continue.",
"zh": {
"title": "最小节省量 = 20,000 Token 才触发压缩",
"description": "自动压缩仅在估算节省量(当前 token 数减去预估摘要大小)超过 20,000 token 时才触发。压缩不是免费的:摘要本身会消耗 token还有生成摘要的 API 调用成本。如果对话只有 25,000 token压缩可能节省 5,000 token但需要一次 API 调用且产出的摘要可能不如原文连贯。20K 的阈值确保只在节省量明显超过开销时才进行压缩。"
"title": "只有摘要返回父循环",
"description": "父循环收到的是压缩后的最终答案,而不是子代理的完整 transcript。这样委派的上下文成本可预测。"
},
"ja": {
"title": "圧縮前に MIN_SAVINGS = 20,000 トークンが必要",
"description": "自動コンパクトは推定節約量現在のトークン数マイナス推定要約サイズが20,000トークンを超えた場合にのみ発動します。圧縮は無料ではありません要約自体がトークンを消費し、さらに生成のための API コール費用がかかります。会話が25,000トークンしかない場合、圧縮で5,000トークン節約できても、API コールが必要で元の会話より一貫性の低い要約になる可能性があります。20K の閾値は、節約量がオーバーヘッドを確実に上回る場合にのみ圧縮を実行することを保証します。"
"title": "親に戻るのは要約だけ",
"description": "親が受け取るのは子の完全な transcript ではなく、圧縮された最終回答です。委任のコンテキストコストを予測可能にします。"
}
},
{
"id": "summary-replaces-all",
"title": "Summary Replaces ALL Messages, Not Partial History",
"description": "When auto_compact fires, it generates a summary and replaces the ENTIRE message history with that summary. It does not keep the last N messages alongside the summary. This avoids a subtle coherence problem: if you keep recent messages plus a summary of older ones, the model sees two representations of overlapping content. The summary might say 'we decided to use approach X' while a recent message still shows the deliberation process, creating contradictory signals. A clean summary is a single coherent narrative.",
"alternatives": "Keeping the last 5-10 messages alongside the summary preserves recent detail and gives the model more to work with. But it creates the overlap problem described above, and makes the total context size less predictable. Some systems use a 'sliding window + summary' approach which works but requires careful tuning of the overlap region.",
"id": "no-recursive-task-tool",
"title": "Subagents Cannot Spawn Subagents",
"description": "The child tool set omits task, preventing recursive delegation from exploding. The lesson keeps isolation visible before adding richer team behavior later.",
"alternatives": "Allowing recursion is powerful, but much harder to bound and explain in a teaching runtime.",
"zh": {
"title": "摘要替换全部消息,而非保留部分历史",
"description": "自动压缩触发时,生成摘要并替换全部消息历史,不会在摘要旁保留最近的 N 条消息。这避免了一个微妙的连贯性问题:如果同时保留近期消息和旧消息的摘要,模型会看到重叠内容的两种表示。摘要可能说'我们决定使用方案 X',而近期消息仍在展示讨论过程,产生矛盾信号。干净的摘要是一个连贯的单一叙述。"
"title": "子代理不能再创建子代理",
"description": "子代理工具集中不包含 task避免递归委派失控。课程先把隔离讲清楚再在后续章节加入更复杂团队行为。"
},
"ja": {
"title": "要約が部分的な履歴ではなく全メッセージを置換",
"description": "自動コンパクトが発動すると、要約を生成してメッセージ履歴の全体をその要約で置換します。要約と並べて直近 N 件のメッセージを保持することはしません。これにより微妙な一貫性の問題を回避します直近のメッセージと古いメッセージの要約を併存させると、モデルは重複するコンテンツの2つの表現を見ることになります。要約が「アプローチ X を使うことに決めた」と言う一方で、直近のメッセージにはまだ検討過程が表示されているかもしれず、矛盾するシグナルを生じます。クリーンな要約は単一の一貫した物語です。"
}
},
{
"id": "transcript-archival",
"title": "Full Conversation Archived to JSONL on Disk",
"description": "Even though context is compressed in memory, the full uncompressed conversation is appended to a JSONL file on disk. Every message, every tool call, every result -- nothing is lost. This means compression is a lossy operation on the in-memory context but a lossless operation on the permanent record. Post-hoc analysis (debugging agent behavior, computing token usage, training data extraction) can always work from the complete transcript. The JSONL format is append-only, making it safe for concurrent writes and easy to stream-process.",
"alternatives": "Not archiving saves disk space but makes debugging hard -- when the agent makes a mistake, you can't see what it was 'thinking' 200 messages ago because that context was compressed away. Database storage (SQLite) would provide queryability but adds a dependency. JSONL is the simplest format that supports append-only writes and line-by-line processing.",
"zh": {
"title": "完整对话以 JSONL 格式归档到磁盘",
"description": "尽管上下文在内存中被压缩,完整的未压缩对话仍会追加到磁盘上的 JSONL 文件中。每条消息、每次工具调用、每个结果都不会丢失。压缩对内存上下文是有损操作,但对永久记录是无损的。事后分析(调试 agent 行为、计算 token 用量、提取训练数据始终可以基于完整记录进行。JSONL 格式仅追加写入,对并发写入安全,易于流式处理。"
},
"ja": {
"title": "完全な会話を JSONL としてディスクに保存",
"description": "メモリ上でコンテキストが圧縮されても、完全な非圧縮会話はディスク上の JSONL ファイルに追記されます。全てのメッセージ、全てのツール呼び出し、全ての結果――何も失われません。圧縮はインメモリコンテキストに対しては不可逆ですが、永続記録に対しては可逆です。事後分析エージェントの挙動デバッグ、トークン使用量の計算、学習データの抽出は常に完全な記録から行えます。JSONL フォーマットは追記専用で、並行書き込みに安全であり行単位の処理が容易です。"
"title": "サブエージェントはさらにサブエージェントを作れない",
"description": "子のツールセットから task を外し、再帰的委任の爆発を防ぎます。まず隔離を明確にし、後の章でより豊かなチーム動作を扱います。"
}
}
]

View File

@@ -2,59 +2,45 @@
"version": "s07",
"decisions": [
{
"id": "file-based-persistence",
"title": "Tasks Stored as JSON Files, Not In-Memory",
"description": "Tasks are persisted as JSON files in a .tasks/ directory on the filesystem instead of being held in memory. This has three critical benefits: (1) Tasks survive process crashes -- if the agent dies mid-task, the task board is still on disk when it restarts. (2) Multiple agents can read and write to the same task directory, enabling multi-agent coordination without shared memory. (3) Humans can inspect and manually edit task files for debugging. The filesystem becomes the shared database.",
"alternatives": "In-memory storage (like v2's TodoWrite) is simpler and faster but loses state on crash and doesn't work across multiple agent processes. A proper database (SQLite, Redis) would provide ACID guarantees and better concurrency, but adds a dependency and operational complexity. Files are the zero-dependency persistence layer that works everywhere.",
"id": "catalog-first",
"title": "Inject a Skill Catalog First",
"description": "The system prompt includes only skill names and short descriptions. The model can discover capabilities without paying for every full SKILL.md on every turn.",
"alternatives": "Loading all skills upfront is simple, but it wastes context and makes unrelated instructions compete.",
"zh": {
"title": "任务存储为 JSON 文件,而非内存",
"description": "任务以 JSON 文件形式持久化在 .tasks/ 目录中,而非保存在内存里。这有三个关键好处:(1) 任务在进程崩溃后仍然存在——如果 agent 在任务中途崩溃,重启后任务板仍在磁盘上;(2) 多个 agent 可以读写同一任务目录,无需共享内存即可实现多代理协调;(3) 人类可以查看和手动编辑任务文件来调试。文件系统就是共享数据库。"
"title": "先注入技能目录",
"description": "系统提示词只包含技能名和简短描述。模型能发现能力,但不用每轮都为完整 SKILL.md 付出上下文成本。"
},
"ja": {
"title": "タスクをメモリではなく JSON ファイルとして保存",
"description": "タスクはメモリ内ではなく .tasks/ ディレクトリに JSON ファイルとして永続化されます。3つの重要な利点があります(1) プロセスのクラッシュ後もタスクが存続する――エージェントがタスク途中でクラッシュしても、再起動時にタスクボードはディスク上に残っています。(2) 複数のエージェントが同じタスクディレクトリを読み書きでき、共有メモリなしにマルチエージェント連携が可能になります。(3) 人間がデバッグのためにタスクファイルを検査・手動編集できます。ファイルシステムが共有データベースになります。"
"title": "まずスキルカタログを注入する",
"description": "システムプロンプトにはスキル名と短い説明だけを入れます。モデルは能力を発見できますが、毎ターン全 SKILL.md を読む必要はありません。"
}
},
{
"id": "dependency-graph",
"title": "Tasks Have blocks/blockedBy Dependency Fields",
"description": "Each task can declare which other tasks it blocks (downstream dependents) and which tasks block it (upstream dependencies). An agent will not start a task that has unresolved blockedBy dependencies. This is essential for multi-agent coordination: when Agent A is writing the database schema and Agent B needs to write queries against it, Agent B's task is blockedBy Agent A's task. Without dependencies, both agents might start simultaneously and Agent B would work against a schema that doesn't exist yet.",
"alternatives": "Simple priority ordering (high/medium/low) doesn't capture 'task B literally cannot start until task A finishes.' A centralized coordinator that assigns tasks in order would work but creates a single point of failure and bottleneck. Declarative dependencies let each agent independently determine what it can work on by reading the task files.",
"id": "full-skill-via-tool-result",
"title": "Full Skills Arrive Through tool_result",
"description": "load_skill returns full instructions as a tool result. That makes skill loading an explicit event in the transcript and keeps it compatible with the normal loop.",
"alternatives": "Mutating the system prompt mid-turn would hide when the model gained new instructions.",
"zh": {
"title": "任务具有 blocks/blockedBy 依赖字段",
"description": "每个任务可以声明它阻塞哪些任务下游依赖以及它被哪些任务阻塞上游依赖。Agent 不会开始有未解决 blockedBy 依赖的任务。这对多代理协调至关重要:当 Agent A 在编写数据库 schema、Agent B 需要写查询时Agent B 的任务被 Agent A 的任务阻塞。没有依赖关系,两个 agent 可能同时开始,而 Agent B 会针对一个尚不存在的 schema 工作。"
"title": "完整技能通过 tool_result 进入",
"description": "load_skill 把完整说明作为 tool result 返回。这样技能加载是 transcript 中的显式事件,也兼容普通循环。"
},
"ja": {
"title": "タスクに blocks/blockedBy 依存関係フィールド",
"description": "各タスクは、自分がブロックするタスク(下流の依存先)と、自分をブロックするタスク(上流の依存元)を宣言できます。エージェントは未解決の blockedBy 依存がある タスクを開始しません。これはマルチエージェント連携に不可欠です:エージェント A がデータベーススキーマを書いていてエージェント B がそれに対するクエリを書く必要がある場合、B のタスクは A のタスクにブロックされます。依存関係がなければ両エージェントが同時に開始し、B はまだ存在しないスキーマに対して作業することになります。"
"title": "完全なスキルは tool_result で届く",
"description": "load_skill は完全な指示を tool result として返します。スキル読み込みが transcript 上の明示的なイベントになり、通常ループと互換です。"
}
},
{
"id": "task-default-todo-coexistence",
"title": "Task as Course Default, Todo Still Useful",
"description": "TaskManager extends the Todo mental model and becomes the default workflow from s07 onward in this course. Both track work items with statuses, but TaskManager adds file persistence (survives crashes), dependency tracking (blocks/blockedBy), ownership fields, and multi-process coordination. Todo remains useful for short, linear, one-shot tracking where heavyweight coordination is unnecessary.",
"alternatives": "Using only Todo keeps the model minimal but weak for long-running or collaborative work. Using only Task everywhere maximizes consistency but can feel heavy for tiny one-off tasks.",
"id": "filesystem-skills",
"title": "Skills Are Files, Not Code Plugins",
"description": "A skill is a directory with SKILL.md. Keeping skills as files makes authoring, reviewing, and loading knowledge straightforward.",
"alternatives": "Executable plugins can do more, but this chapter is about instruction loading rather than new runtime code.",
"zh": {
"title": "Task 为课程主线Todo 仍有适用场景",
"description": "TaskManager 延续了 Todo 的心智模型,并在本课程 s07 之后成为默认主线。两者都管理带状态的任务项,但 TaskManager 增加了文件持久化崩溃后可恢复、依赖追踪blocks/blockedBy、owner 字段与多进程协作能力。Todo 仍适合短、线性、一次性的轻量跟踪。"
"title": "技能是文件,不是代码插件",
"description": "一个 skill 就是包含 SKILL.md 的目录。把技能保持为文件,便于编写、审查和加载知识。"
},
"ja": {
"title": "Task を主線にしつつ Todo も併存",
"description": "TaskManager は Todo のメンタルモデルを拡張し、本コースでは s07 以降のデフォルトになる。どちらもステータス付き作業項目を扱うが、TaskManager にはファイル永続化クラッシュ耐性、依存関係追跡blocks/blockedBy、owner、マルチプロセス協調がある。Todo は短く直線的な単発作業では引き続き有効。"
}
},
{
"id": "task-write-discipline",
"title": "Durability Needs Write Discipline",
"description": "File persistence reduces context loss, but it does not remove concurrent-write risks by itself. Before writing task state, reload the JSON, validate expected status/dependency fields, and then save atomically. This prevents one agent from silently overwriting another agent's transition.",
"alternatives": "Blind overwrite writes are simpler but can corrupt coordination state under parallel execution. A database with optimistic locking would enforce stronger safety, but the course keeps file-based state for zero-dependency teaching.",
"zh": {
"title": "持久化仍需要写入纪律",
"description": "文件持久化能降低上下文丢失,但不会自动消除并发写入风险。写任务状态前应先重读 JSON、校验 `status/blockedBy` 是否符合预期,再原子写回,避免不同 agent 悄悄覆盖彼此状态。"
},
"ja": {
"title": "耐久性には書き込み規律が必要",
"description": "ファイル永続化だけでは並行書き込み競合は防げない。更新前に JSON を再読込し、`status/blockedBy` を検証して原子的に保存することで、他エージェントの遷移上書きを防ぐ。"
"title": "スキルはコードプラグインではなくファイル",
"description": "スキルは SKILL.md を持つディレクトリです。ファイルとして扱うことで、作成、レビュー、知識読み込みが分かりやすくなります。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s08",
"decisions": [
{
"id": "notification-bus",
"title": "threading.Queue as the Notification Bus",
"description": "Background task results are delivered via a threading.Queue instead of direct callbacks. The background thread puts a notification on the queue when its work completes. The main agent loop polls the queue before each LLM call. This decoupling is important: the background thread doesn't need to know anything about the main loop's state or timing. It just drops a message on the queue and moves on. The main loop picks it up at its own pace -- never mid-API-call, never mid-tool-execution. No race conditions, no callback hell.",
"alternatives": "Direct callbacks (background thread calls a function in the main thread) would deliver results faster but create thread-safety issues -- the callback might fire while the main thread is in the middle of building a request. Event-driven systems (asyncio, event emitters) work but add complexity. A queue is the simplest thread-safe communication primitive.",
"id": "cheap-first-compaction",
"title": "Compaction Runs Cheap Passes First",
"description": "Budgeting large tool results, snipping middle history, and replacing old tool outputs run before any LLM summary. Most turns should avoid an extra model call.",
"alternatives": "Summarizing immediately is conceptually simple, but expensive and often unnecessary.",
"zh": {
"title": "用 threading.Queue 作为通知总线",
"description": "后台任务结果通过 threading.Queue 传递,而非直接回调。后台线程在工作完成时向队列放入通知,主 agent 循环在每次 LLM 调用前轮询队列。这种解耦很重要:后台线程无需了解主循环的状态或时序,只需往队列放入消息然后继续。主循环按自己的节奏取出消息——永远不会在 API 调用中途或工具执行中途。没有竞争条件,没有回调地狱。"
"title": "压缩先跑低成本步骤",
"description": "大工具结果预算、中段裁剪、旧工具输出占位会先于任何 LLM 摘要运行。多数回合不应该额外调用模型。"
},
"ja": {
"title": "threading.Queue を通知バスとして使用",
"description": "バックグラウンドタスクの結果は直接コールバックではなく threading.Queue を通じて配信されます。バックグラウンドスレッドは作業完了時にキューに通知を投入します。メインのエージェントループは各 LLM 呼び出しの前にキューをポーリングします。この疎結合が重要ですバックグラウンドスレッドはメインループの状態やタイミングを一切知る必要がありません。キューにメッセージを入れて先に進むだけです。メインループは自分のペースで取り出します――API 呼び出しの途中でもツール実行の途中でもありません。レースコンディションもコールバック地獄もありません。"
"title": "圧縮は低コストな処理から行う",
"description": "大きな tool result の退避、中間履歴の切り詰め、古い出力の置換を LLM 要約より先に行います。多くのターンで追加モデル呼び出しを避けられます。"
}
},
{
"id": "daemon-threads",
"title": "Background Tasks Run as Daemon Threads",
"description": "Background task threads are created with daemon=True. In Python, daemon threads are killed automatically when the main thread exits. This prevents a common problem: if the main agent completes its work and exits, but a background thread is still running (waiting on a long API call, stuck in a loop), the process would hang indefinitely. With daemon threads, exit is clean -- the main thread finishes, all daemon threads die, process exits. No zombie processes, no cleanup code needed.",
"alternatives": "Non-daemon threads with explicit cleanup (join with timeout, then terminate) give more control over shutdown but require careful lifecycle management. Process-based parallelism (multiprocessing) provides stronger isolation but higher overhead. Daemon threads are the pragmatic choice: minimal code, correct behavior in the common case.",
"id": "large-output-side-store",
"title": "Large Outputs Move to a Side Store",
"description": "The transcript keeps a pointer while the full output is persisted elsewhere. This preserves recoverability without forcing the model to reread huge logs.",
"alternatives": "Dropping output loses evidence; keeping all output burns context.",
"zh": {
"title": "后台任务以守护线程运行",
"description": "后台任务线程以 daemon=True 创建。在 Python 中,守护线程在主线程退出时自动被终止。这防止了一个常见问题:如果主 agent 完成工作并退出,但后台线程仍在运行(等待一个长时间 API 调用或陷入循环),进程会无限挂起。使用守护线程,退出是干净的——主线程结束,所有守护线程自动终止,进程退出。没有僵尸进程,不需要清理代码。"
"title": "大输出转移到旁路存储",
"description": "Transcript 保留指针,完整输出持久化到别处。这样既能恢复证据,又不会强迫模型反复读取巨量日志。"
},
"ja": {
"title": "バックグラウンドタスクはデーモンスレッドとして実行",
"description": "バックグラウンドタスクのスレッドは daemon=True で作成されます。Python ではデーモンスレッドはメインスレッドの終了時に自動的に終了されます。これにより一般的な問題を防ぎます:メインエージェントが作業を完了して終了しても、バックグラウンドスレッドがまだ実行中(長い API 呼び出しを待機、ループに陥っている)だとプロセスが無限にハングします。デーモンスレッドならクリーンに終了できます――メインスレッドが終了すると全デーモンスレッドが自動終了し、プロセスが終了します。ゾンビプロセスもクリーンアップコードも不要です。"
"title": "大きな出力はサイドストアへ移す",
"description": "transcript にはポインタだけを残し、完全な出力は別に保存します。証拠を失わず、モデルが巨大ログを読み直す必要もありません。"
}
},
{
"id": "attachment-format",
"title": "Structured Notification Format with Type Tags",
"description": "Notifications from background tasks use a structured format: {\"type\": \"attachment\", \"attachment\": {status, result, ...}} instead of plain text strings. The type tag lets the main loop handle different notification types differently: an 'attachment' might be injected into the conversation as a tool_result, while a 'status_update' might just update a progress indicator. Machine-readable notifications also enable programmatic filtering (show only errors, suppress progress updates) and UI rendering (display status as a progress bar, not raw text).",
"alternatives": "Plain text notifications are simpler but lose structure. The main loop would have to parse free-form text to determine what happened, which is fragile. A class hierarchy (StatusNotification, ResultNotification, ErrorNotification) is more Pythonic but less portable -- JSON structures work the same way regardless of language or serialization format.",
"id": "reactive-compact",
"title": "Reactive Compact Handles API Rejection",
"description": "Even after estimates, the API may reject a prompt as too long. reactive_compact is the emergency path that compacts and retries.",
"alternatives": "Treating prompt_too_long as fatal would make long sessions brittle.",
"zh": {
"title": "带类型标签的结构化通知格式",
"description": "后台任务的通知使用结构化格式:{\"type\": \"attachment\", \"attachment\": {status, result, ...}}而非纯文本字符串。类型标签让主循环可以区别处理不同通知类型attachment 可能作为 tool_result 注入对话,而 status_update 可能只更新进度指示器。机器可读的通知还支持程序化过滤(只显示错误、抑制进度更新)和 UI 渲染(将状态显示为进度条而非原始文本)。"
"title": "Reactive Compact 处理 API 拒绝",
"description": "即使有估算API 仍可能拒绝过长 prompt。reactive_compact 是压缩并重试的应急路径。"
},
"ja": {
"title": "型タグ付き構造化通知フォーマット",
"description": "バックグラウンドタスクからの通知は構造化フォーマットを使用します:プレーンテキストではなく {\"type\": \"attachment\", \"attachment\": {status, result, ...}} です。型タグによりメインループは異なる通知タイプを異なる方法で処理できますattachment は会話に tool_result として注入され、status_update は進捗インジケーターの更新のみを行うかもしれません。機械可読な通知はプログラム的なフィルタリング(エラーのみ表示、進捗更新の抑制)や UI レンダリング(ステータスを生テキストではなくプログレスバーとして表示)も可能にします。"
"title": "Reactive compact が API 拒否を処理する",
"description": "見積もり後でも API が prompt_too_long を返すことがあります。reactive_compact は圧縮して再試行する緊急経路です。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s09",
"decisions": [
{
"id": "teammate-vs-subagent",
"title": "Persistent Teammates vs One-Shot Subagents",
"description": "In s04, subagents are ephemeral: spawn, do one task, return result, die. Their knowledge dies with them. In s09, teammates are persistent threads with identity (name, role) and config files. A teammate can complete task A, then be assigned task B, carrying forward everything it learned. Persistent teammates accumulate project knowledge, understand established patterns, and don't need to re-read the same files for every task.",
"alternatives": "One-shot subagents (s04 style) are simpler and provide perfect context isolation -- no risk of one task's context polluting another. But the re-learning cost is high: every new task starts from zero. A middle ground (subagents with shared memory/knowledge base) was considered but adds complexity without the full benefit of persistent identity and state.",
"id": "memory-index",
"title": "A Small Index Keeps Memory Cheap",
"description": "MEMORY.md stays short and always present. Full memory files are loaded only when the index suggests they are relevant.",
"alternatives": "Injecting all memories every turn would turn long-term knowledge into context bloat.",
"zh": {
"title": "持久化队友 vs 一次性 Subagent",
"description": "在 s04 中Subagent 是临时的:创建、执行一个任务、返回结果、销毁。它们的知识随之消亡。在 s09 中,队友是具有身份(名称、角色)和配置文件的持久化线程。队友可以完成任务 A然后被分配任务 B并携带之前学到的所有知识。持久化队友积累项目知识理解已建立的模式不需要为每个任务重新阅读相同的文件。"
"title": "小索引让记忆保持低成本",
"description": "MEMORY.md 保持短小并始终存在。只有当索引显示相关时,才加载完整记忆文件。"
},
"ja": {
"title": "永続的なチームメイト vs 使い捨てサブエージェント",
"description": "s04 ではサブエージェントは一時的です生成、1つのタスクを実行、結果を返却、消滅。その知識も一緒に消えます。s09 ではチームメイトはアイデンティティ(名前、役割)と設定ファイルを持つ永続的なスレッドです。チームメイトはタスク A を完了した後、学んだ全てを引き継いでタスク B に割り当てられます。永続的なチームメイトはプロジェクトの知識を蓄積し、確立されたパターンを理解し、タスクごとに同じファイルを再読する必要がありません。"
"title": "小さな索引でメモリを低コストに保つ",
"description": "MEMORY.md は短く常に存在します。索引が関連性を示す場合だけ、完全なメモリファイルを読み込みます。"
}
},
{
"id": "file-based-team-config",
"title": "Team Config Persisted to .teams/{name}/config.json",
"description": "Team structure (member names, roles, agent IDs) is stored in a JSON config file, not in any agent's memory. Any agent can discover its teammates by reading the config file -- no need for a discovery service or shared memory. If an agent crashes and restarts, it reads the config to find out who else is on the team. This is consistent with the s07 philosophy: the filesystem is the coordination layer.",
"alternatives": "In-memory team registries are faster but don't survive process restarts and require a central process to maintain. Service discovery (like DNS or a discovery server) is more robust at scale but overkill for a local multi-agent system. File-based config is the simplest approach that works across independent processes.",
"id": "extract-after-turn",
"title": "Memory Extraction Happens After the Turn",
"description": "The agent first solves the user's task, then extracts durable facts from the original messages. This avoids interrupting the working loop with memory bookkeeping.",
"alternatives": "Writing memory during every tool step would make the loop noisy and increase accidental persistence.",
"zh": {
"title": "团队配置持久化到 .teams/{name}/config.json",
"description": "团队结构成员名称、角色、agent ID存储在 JSON 配置文件中,而非任何 agent 的内存中。任何 agent 都可以通过读取配置文件发现队友——无需发现服务或共享内存。如果 agent 崩溃并重启,它读取配置即可知道团队中还有谁。这与 s07 的理念一致:文件系统就是协调层。配置文件人类可读,便于手动添加或移除团队成员、调试团队配置问题。"
"title": "记忆提取发生在回合之后",
"description": "Agent 先完成用户任务,再从原始消息中提取持久事实。这样不会用记忆账务打断工作循环。"
},
"ja": {
"title": "チーム設定を .teams/{name}/config.json に永続化",
"description": "チーム構成(メンバー名、役割、エージェント IDはエージェントのメモリではなく JSON 設定ファイルに保存されます。どのエージェントも設定ファイルを読むことでチームメイトを発見できます――ディスカバリーサービスや共有メモリは不要です。エージェントがクラッシュして再起動した場合、設定を読んで他のチームメンバーを把握します。これは s07 の思想と一貫しています:ファイルシステムが連携レイヤーです。"
"title": "メモリ抽出はターン後に行う",
"description": "エージェントはまずユーザーのタスクを解き、その後で元メッセージから永続的な事実を抽出します。作業ループをメモリ管理で中断しません。"
}
},
{
"id": "tool-filtering-by-role",
"title": "Teammates Get Subset of Tools, Lead Gets All",
"description": "The team lead receives ALL_TOOLS (including spawn, send, read_inbox, etc.) while teammates receive TEAMMATE_TOOLS (a reduced set focused on task execution). This enforces a clear separation of concerns: teammates focus on doing work (coding, testing, researching), while the lead focuses on coordination (creating tasks, assigning work, managing communication). Giving teammates coordination tools would let them create their own sub-teams or reassign tasks, undermining the lead's ability to maintain a coherent plan.",
"alternatives": "Giving all agents identical tools is simpler and more egalitarian, but in practice leads to coordination chaos -- multiple agents trying to manage each other, creating conflicting task assignments. Static role-based filtering is predictable and easy to reason about.",
"id": "dream-consolidation",
"title": "Consolidation Is Periodic, Not Constant",
"description": "Dream-style consolidation can merge and prune memories periodically. The runtime separates everyday retrieval from heavier memory maintenance.",
"alternatives": "Consolidating on every turn is costly and risks rewriting memories before they prove useful.",
"zh": {
"title": "队友获得工具子集,组长获得全部工具",
"description": "团队组长获得 ALL_TOOLS包括 spawn、send、read_inbox 等),而队友获得 TEAMMATE_TOOLS专注于任务执行的精简工具集。这强制了清晰的职责分离队友专注于做事编码、测试、研究组长专注于协调创建任务、分配工作、管理沟通。给队友协调工具会让他们创建自己的子团队或重新分配任务破坏组长维持连贯计划的能力。"
"title": "整合是周期性的,不是每回合都做",
"description": "Dream 式整合可以周期性合并和修剪记忆。运行时把日常检索和更重的记忆维护分开。"
},
"ja": {
"title": "チームメイトはツールのサブセット、リーダーは全ツール",
"description": "チームリーダーは ALL_TOOLSspawn、send、read_inbox など含む)を受け取り、チームメイトは TEAMMATE_TOOLSタスク実行に特化した縮小セットを受け取ります。これにより明確な関心の分離が強制されますチームメイトは作業コーディング、テスト、調査に集中し、リーダーは調整タスク作成、作業割り当て、コミュニケーション管理に集中します。"
"title": "統合は常時ではなく定期的に行う",
"description": "Dream 型の統合はメモリの結合や剪定を定期的に行います。日常の検索と重いメモリ保守を分離します。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s10",
"decisions": [
{
"id": "jsonl-inbox",
"title": "JSONL Inbox Files Instead of Shared Memory",
"description": "Each teammate has its own inbox file (a JSONL file in the team directory). Sending a message means appending a JSON line to the recipient's inbox file. Reading messages means reading the inbox file and tracking which line was last read. JSONL is append-only by nature, which means concurrent writers don't corrupt each other's data (appends to different file positions). This works across processes without any shared memory, mutex, or IPC mechanism. It's also crash-safe: if the writer crashes mid-append, the worst case is one partial line that the reader can skip.",
"alternatives": "Shared memory (Python multiprocessing.Queue) would be faster but doesn't work if agents are separate processes launched independently. A message broker (Redis, RabbitMQ) provides robust pub/sub but adds infrastructure dependencies. Unix domain sockets would work but are harder to debug (no human-readable message log). JSONL files are the simplest approach that provides persistence, cross-process communication, and debuggability.",
"id": "prompt-from-state",
"title": "The System Prompt Is Built from Runtime State",
"description": "Prompt sections are selected from real state: workspace, available tools, memory, and skills. The prompt 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.",
"zh": {
"title": "JSONL 收件箱文件而非共享内存",
"description": "每个队友都有自己的收件箱文件(团队目录中的 JSONL 文件)。发送消息意味着向接收者的收件箱文件追加一行 JSON。读取消息意味着读取收件箱文件并追踪上次读到的行。JSONL 天然是仅追加的,这意味着并发写入不会破坏彼此的数据(追加到不同的文件位置)。这在无需共享内存、互斥锁或 IPC 机制的情况下跨进程工作。它也是崩溃安全的:如果写入者在追加中途崩溃,最坏情况是一行不完整的数据,读取者可以跳过。"
"title": "系统提示词由运行时状态构建",
"description": "Prompt section 来自真实状态workspace、可用工具、memory 和 skills。提示词是运行时的产物而不是单个硬编码字符串。"
},
"ja": {
"title": "共有メモリではなく JSONL インボックスファイル",
"description": "各チームメイトはチームディレクトリ内に独自のインボックスファイルJSONL ファイル)を持ちます。メッセージの送信は受信者のインボックスファイルに JSON 行を追記することです。メッセージの読み取りはインボックスファイルを読んで最後に読んだ行を追跡することです。JSONL は本質的に追記専用で、並行ライターが互いのデータを破壊しません異なるファイル位置への追記。共有メモリ、ミューテックス、IPC メカニズムなしにプロセス間で動作します。"
"title": "システムプロンプトはランタイム状態から作る",
"description": "workspace、利用可能ツール、memory、skills などの実状態から prompt section を選びます。プロンプトは単一の固定文字列ではなくランタイムの産物です。"
}
},
{
"id": "five-message-types",
"title": "Exactly Five Message Types Cover All Coordination Patterns",
"description": "The messaging system supports exactly five types: (1) 'message' for point-to-point communication between two agents, (2) 'broadcast' for team-wide announcements, (3) 'shutdown_request' for graceful termination, (4) 'shutdown_response' for acknowledging shutdown, (5) 'plan_approval_response' for the lead to approve or reject a teammate's plan. These five types map to the fundamental coordination patterns: direct communication, broadcast, lifecycle management, and approval workflows.",
"alternatives": "A single generic message type with metadata fields would be more flexible but makes it harder to enforce protocol correctness. Many more types (10+) would provide finer-grained semantics but increase the model's decision burden. Five types is the sweet spot where every type has a clear, distinct purpose.",
"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.",
"zh": {
"title": "恰好五种消息类型覆盖所有协调模式",
"description": "消息系统恰好支持五种类型:(1) message 用于两个 agent 间的点对点通信;(2) broadcast 用于全团队公告;(3) shutdown_request 用于优雅终止;(4) shutdown_response 用于确认终止;(5) plan_approval_response 用于组长批准或拒绝队友的计划。这五种类型映射到基本协调模式:直接通信、广播、生命周期管理和审批流程。"
"title": "确定性缓存避免重复组装",
"description": "Context 对象用稳定顺序序列化。如果 key 没变,提示词就可以安全复用。"
},
"ja": {
"title": "正確に5つのメッセージタイプで全連携パターンをカバー",
"description": "メッセージングシステムは正確に5つのタイプをサポートします(1) message は2つのエージェント間のポイントツーポイント通信、(2) broadcast はチーム全体への通知、(3) shutdown_request はグレースフルな終了要求、(4) shutdown_response はシャットダウンの確認応答、(5) plan_approval_response はリーダーによるチームメイトの計画の承認・却下。"
"title": "決定的キャッシュで再組み立てを避ける",
"description": "context オブジェクトを安定した順序でシリアライズします。key が変わらなければプロンプトを安全に再利用できます。"
}
},
{
"id": "inbox-before-api-call",
"title": "Check Inbox Before Every LLM Call",
"description": "Teammates check their inbox file at the top of every agent loop iteration, before calling the LLM API. This ensures maximum responsiveness to incoming messages: a shutdown request is seen within one loop iteration (typically seconds), not after the current task completes (potentially minutes). The inbox check is cheap (read a small file, check if new lines exist) compared to the LLM call (seconds of latency, thousands of tokens). This placement also means incoming messages can influence the next LLM call -- a message saying 'stop working on X, switch to Y' takes effect immediately.",
"alternatives": "Checking inbox after each tool execution would be more responsive but adds overhead to every tool call, which is more frequent than LLM calls. A separate watcher thread could monitor the inbox continuously but adds threading complexity. Checking once per LLM call is the pragmatic sweet spot: responsive enough for coordination, cheap enough to not impact performance.",
"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.",
"zh": {
"title": "每次 LLM 调用前检查收件箱",
"description": "队友在每次 agent 循环迭代的顶部、调用 LLM API 之前检查收件箱文件。这确保了对传入消息的最大响应性:一个终止请求会在一个循环迭代内被看到(通常几秒钟),而非在当前任务完成后(可能数分钟)。收件箱检查成本很低(读取小文件,检查是否有新行),相比 LLM 调用(秒级延迟,数千 token微不足道。这个位置还意味着传入消息可以影响下一次 LLM 调用——一条'停止 X转去做 Y'的消息会立即生效。"
"title": "Prompt Section 有明确归属",
"description": "identity、tools、workspace、memory 是分开的 section。这样更容易定位哪一层注入了错误指令。"
},
"ja": {
"title": "毎回の LLM 呼び出し前にインボックスを確認",
"description": "チームメイトはエージェントループの各イテレーションの冒頭、LLM API を呼び出す前にインボックスファイルを確認します。これにより受信メッセージへの応答性を最大化しますシャットダウンリクエストは1ループイテレーション以内通常数秒で確認され、現在のタスク完了後数分かかる可能性ではありません。"
"title": "prompt section には所有者がある",
"description": "identity、tools、workspace、memory を別 section にします。どのサブシステムが悪い指示を入れたかを追いやすくなります。"
}
}
]

View File

@@ -2,45 +2,45 @@
"version": "s11",
"decisions": [
{
"id": "polling-not-events",
"title": "Polling for Unclaimed Tasks Instead of Event-Driven Notification",
"description": "Autonomous teammates poll the shared task board every ~1 second to find unclaimed tasks, rather than waiting for event-driven notifications. Polling is fundamentally simpler than pub/sub: there's no subscription management, no event routing, no missed-event bugs. With file-based persistence, polling is just 'read the directory listing' -- a cheap operation that works regardless of how many agents are running. The 1-second interval balances responsiveness (new tasks are discovered quickly) against filesystem overhead (not hammering the disk with reads).",
"alternatives": "Event-driven notification (file watchers via inotify/fsevents, or a pub/sub channel) would reduce latency from seconds to milliseconds. But file watchers are platform-specific and unreliable across network filesystems. A message broker would work but adds infrastructure. For a system where tasks take minutes to complete, discovering new tasks in 1 second instead of 10 milliseconds makes no practical difference.",
"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.",
"zh": {
"title": "轮询未认领任务而非事件驱动通知",
"description": "自主队友每隔约 1 秒轮询共享任务板以寻找未认领的任务,而非等待事件驱动的通知。轮询从根本上比发布/订阅更简单:没有订阅管理、没有事件路由、没有事件丢失的 bug。在基于文件的持久化下轮询就是'读取目录列表'——一个低成本操作,无论有多少 agent 在运行都能正常工作。1 秒的间隔平衡了响应性(新任务被快速发现)和文件系统开销(不会过度读取磁盘)。"
"title": "不同失败需要不同恢复路径",
"description": "max_tokens、prompt_too_long 和供应商过载含义不同。运行时会先分类错误,再决定如何重试。"
},
"ja": {
"title": "イベント駆動通知ではなくポーリングで未割り当てタスクを発見",
"description": "自律的なチームメイトはイベント駆動の通知を待つのではなく、約1秒ごとに共有タスクボードをポーリングして未割り当てタスクを探します。ポーリングはパブ/サブより根本的にシンプルです:サブスクリプション管理、イベントルーティング、イベント欠落バグがありません。ファイルベースの永続化では、ポーリングは「ディレクトリ一覧を読む」だけで、実行中のエージェント数に関係なく動作する安価な操作です。"
"title": "失敗ごとに異なる回復経路が必要",
"description": "max_tokens、prompt_too_long、プロバイダ過負荷は意味が違います。ランタイムは再試行前にエラーを分類します。"
}
},
{
"id": "idle-timeout",
"title": "60-Second Idle Timeout Before Self-Termination",
"description": "When an autonomous teammate has no tasks to work on and no messages in its inbox, it waits up to 60 seconds before giving up and shutting down. This prevents zombie teammates that wait forever for work that never comes -- a real problem when the lead forgets to send a shutdown request, or when all remaining tasks are blocked on external events. The 60-second window is long enough that a brief gap between task completions and new task creation won't cause premature shutdown, but short enough that unused teammates don't waste resources.",
"alternatives": "No timeout (wait forever) risks zombie processes. A very short timeout (5s) causes premature exits when the lead is simply thinking or typing. A heartbeat system (lead periodically pings teammates to keep them alive) works but adds protocol complexity. The 60-second fixed timeout is a good default that balances false-positive exits against resource waste.",
"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.",
"zh": {
"title": "空闲 60 秒后自动终止",
"description": "当自主队友没有任务可做且收件箱中没有消息时,它最多等待 60 秒后放弃并关闭。这防止了永远等待不会到来的工作的僵尸队友——这在组长忘记发送关闭请求、或所有剩余任务都被外部事件阻塞时是真实存在的问题。60 秒窗口足够长,不会因为任务完成到新任务创建之间的短暂间隔而导致过早关闭;又足够短,不会让闲置队友浪费资源。"
"title": "RecoveryState 防止无限重试",
"description": "运行时记录 token 升级、compact retry、连续 529、fallback model 等状态。恢复因此有边界、可检查。"
},
"ja": {
"title": "60秒のアイドルタイムアウトで自動終了",
"description": "自律的なチームメイトが作業するタスクもインボックスのメッセージもない場合、最大60秒待ってから諦めてシャットダウンします。これにより永遠に来ない仕事を待ち続けるゾンビチームメイトを防ぎます。60秒のウィンドウはタスク完了から新タスク作成までの短い間隔で早期シャットダウンが起きない十分な長さであり、かつ未使用のチームメイトがリソースを浪費しない十分な短さです。"
"title": "RecoveryState が無限リトライを防ぐ",
"description": "token 拡張、compact retry、連続 529、fallback model の利用を追跡します。回復処理に境界と可観測性を与えます。"
}
},
{
"id": "identity-after-compression",
"title": "Re-Inject Teammate Identity After Context Compression",
"description": "When auto_compact compresses the conversation, the resulting summary loses crucial metadata: the teammate's name, which team it belongs to, and its agent_id. Without this information, the teammate can't claim tasks (tasks are owned by name), can't check its inbox (inbox files are keyed by agent_id), and can't identify itself in messages. So after every auto_compact, the system re-injects a structured identity block into the conversation: 'You are [name] on team [team], your agent_id is [id], your inbox is at [path].' This is the minimum context needed for the teammate to remain functional after memory loss.",
"alternatives": "Putting identity in the system prompt (which survives compression) would avoid this problem, but violates the cache-friendly static-system-prompt design from s05. Embedding identity in the summary prompt ('when summarizing, always include your name and team') is unreliable -- the LLM might omit it. Explicit post-compression injection is deterministic and guaranteed to work.",
"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.",
"zh": {
"title": "上下文压缩后重新注入队友身份",
"description": "自动压缩对话时,生成的摘要会丢失关键元数据:队友的名称、所属团队和 agent_id。没有这些信息队友无法认领任务任务按名称归属、无法检查收件箱收件箱文件以 agent_id 为键)、也无法在消息中表明身份。因此每次自动压缩后,系统会向对话中重新注入一个结构化的身份块:'你是 [team] 团队的 [name],你的 agent_id 是 [id],你的收件箱在 [path]。'这是队友在记忆丢失后保持功能所需的最小上下文。"
"title": "Token 恢复时不伪造用户消息",
"description": "第一次 max_tokens 升级会直接重试,不追加合成 user prompt。Transcript 应反映真实事件,而不是内部恢复技巧。"
},
"ja": {
"title": "コンテキスト圧縮後にチームメイトのアイデンティティを再注入",
"description": "自動コンパクトが会話を圧縮すると、生成された要約は重要なメタデータを失いますチームメイトの名前、所属チーム、agent_id。この情報がなければチームメイトはタスクを申告できずタスクは名前で所有、インボックスを確認できずインボックスファイルは agent_id をキーとする)、メッセージで自分を識別できません。そのため自動コンパクトの後、システムは構造化されたアイデンティティブロックを会話に再注入します。これはメモリ喪失後もチームメイトが機能し続けるために必要な最小限のコンテキストです。"
"title": "トークン回復で偽のユーザーメッセージを入れない",
"description": "最初の max_tokens 拡張では合成 user prompt を追加せず再試行します。transcript は内部の回復処理ではなく実際の出来事を反映すべきです。"
}
}
]

View File

@@ -2,87 +2,45 @@
"version": "s12",
"decisions": [
{
"id": "shared-board-isolated-lanes",
"title": "Shared Task Board + Isolated Execution Lanes",
"description": "The task board remains shared and centralized in `.tasks/`, while file edits happen in per-task worktree directories. This separation preserves global visibility (who owns what, what is done) without forcing everyone to edit inside one mutable directory. Coordination stays simple because there is one board, and execution stays safe because each lane is isolated.",
"alternatives": "A single shared workspace is simpler but causes edit collisions and mixed git state. Fully independent task stores per lane avoid collisions but lose team-level visibility and make planning harder.",
"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": "任务板继续集中`.tasks/`,而文件改动发生在按任务划分的 worktree 目录中。这样既保留了全局可见性(谁在做什么、完成到哪),又避免所有人同时写同一目录导致冲突。协调层简单(一个任务板),执行层安全(多条隔离通道)。"
"title": "任务是持久 JSON 文件",
"description": "每个任务都持久化在 .tasks/ 下,包含 id、subject、description、status、owner、blockedBy。任务板能跨上下文压缩和进程重启保留。"
},
"ja": {
"title": "共有タスクボード + 分離実行レーン",
"description": "タスクボード`.tasks/`集約しつつ、実際の編集はタスクごとの worktree ディレクトリで行う。これにより全体の可視性担当と進捗を維持しながら、単一ディレクトリでの衝突を回避できる。調整は1つのボードで単純化され、実行はレーン分離で安全になる。"
"title": "タスクは永続 JSON ファイル",
"description": "タスクは .tasks/ に id、subject、description、status、owner、blockedBy を持って保存されます。タスクボードはコンテキスト圧縮や再起動を越えて残ります。"
}
},
{
"id": "index-file-lifecycle",
"title": "Explicit Worktree Lifecycle Index",
"description": "`.worktrees/index.json` records each worktree's name, path, branch, task_id, and status. This makes lifecycle state inspectable and recoverable even after context compression or process restarts. The index also provides a deterministic source for list/status/remove operations.",
"alternatives": "Relying only on `git worktree list` removes local bookkeeping but loses task binding metadata and custom lifecycle states. Keeping all state only in memory is simpler in code but breaks recoverability.",
"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": "显式 worktree 生命周期索引",
"description": "`.worktrees/index.json` 记录每个 worktree 的名称、路径、分支、task_id 与状态。即使上下文压缩或进程重启,这些生命周期状态仍可检查和恢复。它也为 list/status/remove 提供了确定性的本地数据源。"
"title": "blockedBy 编码任务顺序",
"description": "只有所有 blockedBy 依赖都完成时,任务才能被 claim。缺失依赖也被视为阻塞采用 fail closed。"
},
"ja": {
"title": "明示的な worktree ライフサイクル索引",
"description": "`.worktrees/index.json` に name/path/branch/task_id/status を記録することで、コンテキスト圧縮やプロセス再起動後も状態を追跡できる。list/status/remove の挙動もこの索引を基準に決定できる。"
"title": "blockedBy が順序を表現する",
"description": "blockedBy の依存がすべて完了した時だけタスクを claim できます。存在しない依存もブロック扱いにして fail closed にします。"
}
},
{
"id": "lane-cwd-routing-and-reentry-guard",
"title": "Lane-Scoped CWD Routing + Re-entry Guard",
"description": "Commands are routed to a worktree's directory via `worktree_run(name, command)` using the `cwd` parameter. A re-entry guard prevents accidentally running inside an already-active worktree context, keeping lifecycle ownership unambiguous.",
"alternatives": "Global cwd mutation is easy to implement but can leak context across parallel work. Allowing silent re-entry makes lifecycle ownership ambiguous and complicates teardown behavior.",
"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": "按通道 cwd 路由 + 禁止重入",
"description": "命令通过 `worktree_run(name, command)` 使用 `cwd` 参数路由到 worktree 目录。重入保护避免了在已激活的 worktree 上下文中意外二次进入,保持生命周期归属清晰。"
"title": "Claim 和 Complete 让工作可观察",
"description": "claim_task 记录 owner 和 in_progress 状态complete_task 标记完成,并报告被解锁的下游任务。"
},
"ja": {
"title": "レーン単位 cwd ルーティング + 再入防止",
"description": "`worktree_run(name, command)` で `cwd` パラメータを使いコマンドを worktree ディレクトリへ転送する。再入ガードにより active な worktree への二重入場を防ぎ、ライフサイクルの帰属を明確に保つ。"
}
},
{
"id": "event-stream-observability",
"title": "Append-Only Lifecycle Event Stream",
"description": "Lifecycle events are appended to `.worktrees/events.jsonl` (`worktree.create.*`, `worktree.remove.*`, `task.completed`). This turns hidden transitions into queryable records and makes failures explicit (`*.failed`) instead of silent.",
"alternatives": "Relying only on console logs is lighter but fragile during long sessions and hard to audit. A full event bus infrastructure is powerful but heavier than needed for this teaching baseline.",
"zh": {
"title": "追加式生命周期事件流",
"description": "生命周期事件写入 `.worktrees/events.jsonl`(如 `worktree.create.*`、`worktree.remove.*`、`task.completed`)。这样状态迁移可查询、可追踪,失败也会以 `*.failed` 显式暴露,而不是静默丢失。"
},
"ja": {
"title": "追記型ライフサイクルイベント",
"description": "ライフサイクルイベントを `.worktrees/events.jsonl` に追記する(`worktree.create.*`、`worktree.remove.*`、`task.completed` など)。遷移が可観測になり、失敗も `*.failed` として明示できる。"
}
},
{
"id": "task-worktree-closeout",
"title": "Close Task and Workspace Together",
"description": "`worktree_remove(..., complete_task=true)` allows a single closeout step: remove the isolated directory and mark the bound task completed. Closeout remains an explicit tool-driven transition (`worktree_keep` / `worktree_remove`) rather than hidden automatic cleanup. This reduces dangling state where a task says done but its temporary lane remains active (or the reverse).",
"alternatives": "Keeping closeout fully manual gives flexibility but increases operational drift. Fully automatic removal on every completion risks deleting a workspace before final review.",
"zh": {
"title": "任务与工作区一起收尾",
"description": "`worktree_remove(..., complete_task=true)` 允许在一个动作里完成收尾:删除隔离目录并把绑定任务标记为 completed。收尾保持为显式工具驱动迁移`worktree_keep` / `worktree_remove`),而不是隐藏的自动清理。这样可减少状态悬挂(任务已完成但临时工作区仍活跃,或反过来)。"
},
"ja": {
"title": "タスクとワークスペースを同時にクローズ",
"description": "`worktree_remove(..., complete_task=true)` により、分離ディレクトリ削除とタスク完了更新を1ステップで実行できる。クローズ処理は `worktree_keep` / `worktree_remove` の明示ツール遷移として扱い、暗黙の自動清掃にはしない。"
}
},
{
"id": "event-stream-side-channel",
"title": "Event Stream Is Observability Side-Channel",
"description": "Lifecycle events improve auditability, but the source of truth remains task/worktree state files. Events should be read as transition traces, not as a replacement state machine.",
"alternatives": "Using logs alone hides structured transitions; using events as the only state source risks drift when replay/repair semantics are undefined.",
"zh": {
"title": "事件流是观测旁路,不是状态机替身",
"description": "生命周期事件提升可审计性,但真实状态源仍是任务/工作区状态文件。事件更适合做迁移轨迹,而不是替代主状态机。"
},
"ja": {
"title": "イベントは観測サイドチャネルであり状態機械の代替ではない",
"description": "ライフサイクルイベントは監査性を高めるが、真の状態源は task/worktree 状態ファイルのまま。イベントは遷移トレースとして扱い、主状態機械の代替にしない。"
"title": "claim と complete が作業を観測可能にする",
"description": "claim_task は owner と in_progress を記録し、complete_task は完了を記録して解放された下流タスクを報告します。"
}
}
]

View File

@@ -0,0 +1,47 @@
{
"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.",
"zh": {
"title": "后台任务是执行模式,而不是新工具",
"description": "课程保留原有工具表面,只在慢操作外增加后台执行标记。这样能清楚看到:同一个 bash 调用既可以阻塞主循环也可以放入线程。Agent 学到的是响应性属于运行时问题,而不是每个慢任务都要发明一个新工具。"
},
"ja": {
"title": "バックグラウンド処理は新ツールではなく実行モード",
"description": "このレッスンでは既存のツール面を保ち、遅い操作にバックグラウンド実行フラグを加えます。同じ bash 呼び出しがループをブロックすることも、スレッドへ移すこともできる点が見えます。応答性はランタイムの責務であり、遅いタスクごとに新しいツールを作る必要はありません。"
}
},
{
"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": "线程完成后以通知形式回到循环",
"description": "后台结果会作为任务通知注入,而不是伪装成立即返回的 tool result。这样保留了对话时间线模型先看到任务已启动之后再看到任务完成。"
},
"ja": {
"title": "完了したスレッドは通知として戻る",
"description": "バックグラウンド結果は即時の tool result ではなくタスク通知として注入されます。モデルはまず作業開始を見て、その後に完了を知るため、会話の時系列が保たれます。"
}
},
{
"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 would be more production-ready, but it would obscure the minimal moving parts needed to understand threaded agent work.",
"zh": {
"title": "小型共享存储让线程可观察",
"description": "实现用显式字典记录后台任务状态和结果。这样代码仍然易学,同时暴露并发中的关键问题:任务 id、生命周期状态和结果收集。"
},
"ja": {
"title": "小さな共有ストアでスレッドを観測可能にする",
"description": "実装は辞書でバックグラウンドタスクの状態と結果を追跡します。コードを学びやすく保ちながら、id、ライフサイクル、安全な収集という並行処理の要点を示します。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"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.",
"zh": {
"title": "调度器运行在 Agent 循环之外",
"description": "Cron 匹配由独立守护循环处理,而不是让 LLM 记住未来时间。这把计时和推理分开,使定期任务在没有用户对话时也能可靠触发。"
},
"ja": {
"title": "スケジューラはエージェントループの外で動く",
"description": "cron の照合は LLM に未来時刻を覚えさせるのではなく、デーモンループで処理します。時間管理と推論を分離し、ユーザーが会話していない時でも定期処理を確実にします。"
}
},
{
"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": "队列把到期判断和任务执行解耦",
"description": "当 schedule 匹配时,调度器只把任务放入队列,由队列处理器调用 agent_loop。这样 cron 匹配保持快速,长时间运行的 agent 任务不会阻塞后续调度检查。"
},
"ja": {
"title": "キューが期限判定と実行を分離する",
"description": "スケジュールが一致すると、スケジューラは作業をキューへ入れ、キュープロセッサが agent_loop を呼び出します。cron 照合は速く保たれ、長いエージェント実行が次の確認を妨げません。"
}
},
{
"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": "计划任务是持久数据",
"description": "Cron job 存储在小型 JSON 文件中,因此进程重启后仍然存在。课程把计划任务视为可列出、可取消、可检查的数据,而不是隐藏的计时器。"
},
"ja": {
"title": "スケジュールは永続データ",
"description": "cron ジョブは小さな JSON ファイルに保存され、プロセス再起動後も残ります。予定された作業を、一覧化、取り消し、検査できるデータとして扱います。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s15",
"decisions": [
{
"id": "lead-agent-owns-coordination",
"title": "The Lead Owns Coordination",
"description": "The lead agent decides when to spawn teammates, what to send them, and how to interpret replies. Teammates can work independently, but the user-facing conversation stays anchored in one lead loop.",
"alternatives": "A peer-to-peer team would be more flexible, but much harder to explain because no single loop owns the answer.",
"zh": {
"title": "由 Lead Agent 负责协调",
"description": "Lead agent 决定何时创建队友、发送什么任务、如何解释回复。队友可以独立工作,但面向用户的对话始终锚定在一个 lead 循环中。"
},
"ja": {
"title": "調整はリードエージェントが担う",
"description": "リードエージェントがチームメイトの生成、送信内容、返信の解釈を決めます。チームメイトは独立して作業できますが、ユーザー向けの会話は一つのリードループに固定されます。"
}
},
{
"id": "file-backed-mailboxes",
"title": "Mailboxes Make Team Communication Inspectable",
"description": "MessageBus writes JSONL mailboxes so every handoff is visible on disk. This avoids magical shared memory and gives learners a concrete artifact for debugging team behavior.",
"alternatives": "In-memory channels are faster, but they hide the communication history and disappear when the process stops.",
"zh": {
"title": "邮箱文件让团队通信可检查",
"description": "MessageBus 使用 JSONL 邮箱记录每次交接。这样避免了神秘的共享内存,也给学习者一个能直接调试团队行为的具体文件。"
},
"ja": {
"title": "メールボックスでチーム通信を検査可能にする",
"description": "MessageBus は JSONL メールボックスへ各ハンドオフを書き込みます。見えない共有メモリを避け、チーム動作をデバッグできる具体的な成果物を提供します。"
}
},
{
"id": "scoped-teammate-tools",
"title": "Teammates Use Scoped Tool Sets",
"description": "A teammate loop receives a narrower prompt and tool set than the lead. That keeps delegation focused and prevents a helper agent from accidentally taking over orchestration.",
"alternatives": "Giving every teammate the full tool pool is simpler, but it blurs roles and makes failures harder to attribute.",
"zh": {
"title": "队友使用受限工具集",
"description": "队友循环拿到比 lead 更窄的提示词和工具集。这样委派更聚焦,也避免 helper agent 意外接管整体协调。"
},
"ja": {
"title": "チームメイトには範囲を絞ったツールセットを与える",
"description": "チームメイトループにはリードより狭いプロンプトとツールセットを渡します。委任を集中させ、補助エージェントが誤って全体調整を奪うことを防ぎます。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s16",
"decisions": [
{
"id": "typed-protocol-messages",
"title": "Typed Messages Replace Informal Chat",
"description": "Plan requests and shutdown requests are encoded as protocol messages with explicit kinds. The teammate can branch on message type instead of guessing intent from free-form text.",
"alternatives": "Plain natural-language messages are easier to write, but brittle once the team has multiple request types.",
"zh": {
"title": "用类型化协议消息替代随意聊天",
"description": "计划请求和关闭请求会编码成带有明确 kind 的协议消息。队友可以根据消息类型分支处理,而不是从自由文本中猜意图。"
},
"ja": {
"title": "非公式チャットを型付きプロトコルメッセージに置き換える",
"description": "計画要求とシャットダウン要求は明示的な kind を持つプロトコルメッセージとして表現されます。チームメイトは自由文から意図を推測せず、型で分岐できます。"
}
},
{
"id": "request-id-correlation",
"title": "Request IDs Close the Loop",
"description": "Each protocol request creates a pending record with a request_id. Responses must carry the same id, which lets the lead match replies even when multiple teammates are active.",
"alternatives": "Matching by latest message works in demos, but fails as soon as two requests overlap.",
"zh": {
"title": "Request ID 闭合协议循环",
"description": "每个协议请求都会创建带 request_id 的 pending 记录。响应必须携带同一个 id因此即使多个队友同时工作lead 也能匹配对应回复。"
},
"ja": {
"title": "request_id がループを閉じる",
"description": "各プロトコル要求は request_id 付きの pending レコードを作ります。応答も同じ id を持つため、複数のチームメイトが動いていてもリードは対応する返信を照合できます。"
}
},
{
"id": "idle-protocol-handling",
"title": "Protocol Handling Runs During Idle Time",
"description": "Teammates can consume protocol messages while idle, so the lead can request plans or shutdowns without waiting for a separate user turn. This makes team control part of the runtime lifecycle.",
"alternatives": "Only checking protocols during active work would delay control messages and make shutdown unreliable.",
"zh": {
"title": "空闲期也处理协议",
"description": "队友在空闲状态也会消费协议消息,因此 lead 可以请求计划或关闭,而不必等待另一个用户回合。这让团队控制成为运行时生命周期的一部分。"
},
"ja": {
"title": "アイドル中にもプロトコルを処理する",
"description": "チームメイトはアイドル時にもプロトコルメッセージを消費します。リードは別のユーザーターンを待たずに計画や終了を要求でき、チーム制御がランタイムのライフサイクルに組み込まれます。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s17",
"decisions": [
{
"id": "idle-poll-loop",
"title": "Autonomy Starts from Idle Polling",
"description": "The agent becomes autonomous by doing useful checks while idle: scanning tasks, reading inbox messages, and deciding whether to claim work. No new magic planner is introduced.",
"alternatives": "A central scheduler could assign every task, but this lesson focuses on local autonomy inside each teammate loop.",
"zh": {
"title": "自治从空闲轮询开始",
"description": "Agent 通过在空闲时做有用检查获得自治能力:扫描任务、读取 inbox、判断是否 claim 工作。这里没有引入新的神秘规划器。"
},
"ja": {
"title": "自律性はアイドルポーリングから始まる",
"description": "エージェントはアイドル時にタスク走査、受信箱確認、作業の claim 判断を行うことで自律的になります。新しい魔法のプランナーは導入しません。"
}
},
{
"id": "claim-before-work",
"title": "Claim Before Work Prevents Collisions",
"description": "A teammate must claim a task before entering WORK state. Ownership checks make autonomous pickup safe when multiple agents poll the same task board.",
"alternatives": "Agents could simply pick any open task, but two agents might duplicate work or overwrite each other's result.",
"zh": {
"title": "先 Claim 再工作,避免冲突",
"description": "队友必须先 claim 任务,再进入 WORK 状态。多个 agent 轮询同一个任务板时,所有权检查让自治领取任务更安全。"
},
"ja": {
"title": "作業前に claim して衝突を防ぐ",
"description": "チームメイトは WORK 状態へ入る前にタスクを claim します。複数のエージェントが同じタスクボードをポーリングしても、所有権チェックにより安全に取得できます。"
}
},
{
"id": "identity-reinjection",
"title": "Identity Is Re-injected on Each Autonomous Turn",
"description": "Autonomous agents need a stable sense of who they are and what they are allowed to do. Re-injecting identity keeps a teammate from drifting into the lead's responsibilities.",
"alternatives": "A one-time identity prompt is shorter, but long-running loops are prone to context drift.",
"zh": {
"title": "每个自治回合都重新注入身份",
"description": "自治 agent 需要稳定知道自己是谁、允许做什么。重新注入身份可以防止队友逐渐漂移到 lead 的职责上。"
},
"ja": {
"title": "各自律ターンでアイデンティティを再注入する",
"description": "自律エージェントには、自分が誰で何を許可されているかという安定した認識が必要です。アイデンティティを再注入することで、チームメイトがリードの責務へ漂うことを防ぎます。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s18",
"decisions": [
{
"id": "worktree-name-validation",
"title": "Worktree Names Are Validated Before Git Runs",
"description": "The tool validates names before creating branches or directories. That keeps a teaching implementation from normalizing unsafe user input into shell or filesystem operations.",
"alternatives": "Passing names directly to git is shorter, but it turns a collaboration feature into an injection hazard.",
"zh": {
"title": "运行 Git 前先校验 Worktree 名称",
"description": "工具在创建分支或目录前先校验名称。这样教学实现不会把不安全的用户输入直接传入 shell 或文件系统操作。"
},
"ja": {
"title": "git 実行前に worktree 名を検証する",
"description": "ブランチやディレクトリ作成前に名前を検証します。学習用実装が危険なユーザー入力を shell やファイルシステム操作へ流し込むことを防ぎます。"
}
},
{
"id": "task-bound-worktree",
"title": "The Task Record Owns the Worktree Binding",
"description": "A task stores its assigned worktree so future commands know where to run. The binding is explicit data, not a hidden convention based on naming or current working directory.",
"alternatives": "Deriving the worktree path from branch names is convenient, but brittle when tasks are renamed or moved.",
"zh": {
"title": "任务记录持有 Worktree 绑定关系",
"description": "任务会记录自己分配到的 worktree因此后续命令知道应该在哪里运行。这个绑定是显式数据而不是依赖命名或当前目录的隐藏约定。"
},
"ja": {
"title": "タスクレコードが worktree の紐付けを持つ",
"description": "タスクは割り当てられた worktree を保持し、後続コマンドは実行場所を把握できます。この紐付けは命名や現在ディレクトリに依存する暗黙の規約ではなく、明示的なデータです。"
}
},
{
"id": "lifecycle-event-stream",
"title": "Lifecycle Events Stay Separate from Tool Results",
"description": "Creation, status, keep, and removal events are emitted to a side-channel log. That makes worktree state observable without overloading the conversational transcript.",
"alternatives": "Only returning tool results is simpler, but later debugging needs a durable audit trail of worktree lifecycle changes.",
"zh": {
"title": "生命周期事件与工具结果分离",
"description": "创建、状态、保留和移除事件会写入旁路日志。这样 worktree 状态可观察,同时不会把对话 transcript 塞满运行时事件。"
},
"ja": {
"title": "ライフサイクルイベントをツール結果から分離する",
"description": "作成、状態、保持、削除のイベントはサイドチャネルログへ出力します。会話 transcript をランタイムイベントで埋めずに worktree 状態を観測できます。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s19",
"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.",
"zh": {
"title": "MCP 工具使用规范化命名空间",
"description": "发现到的工具会暴露为 mcp__server__tool。前缀让工具来源明确也避免和内置工具或其他服务器工具冲突。"
},
"ja": {
"title": "MCP ツールは正規化された名前空間を使う",
"description": "発見されたツールは mcp__server__tool として公開されます。接頭辞により出所が明確になり、組み込みツールや別サーバーのツールとの衝突を避けます。"
}
},
{
"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": "连接服务器后,运行时会为下一次 LLM 调用组装新的工具池。模型只有在发现阶段让 MCP 工具可见之后,才能调用它们。"
},
"ja": {
"title": "ツール発見がアクティブなツールプールを更新する",
"description": "サーバー接続後、ランタイムは次の LLM 呼び出し用に新しいツールプールを組み立てます。MCP ツールは発見で可視化された後にのみモデルが利用できます。"
}
},
{
"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": "外部结果复用 Tool Result 路径",
"description": "MCP 响应会像普通 tool result 一样追加到对话中。这样 agent 循环无需改变,同时外部系统仍然可以参与。"
},
"ja": {
"title": "外部結果は tool result 経路を再利用する",
"description": "MCP の応答は通常の tool result と同じように会話へ追加されます。エージェントループを変えずに外部システムを参加させられます。"
}
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": "s20",
"decisions": [
{
"id": "composition-over-new-loop",
"title": "The Final Agent Composes Previous Layers",
"description": "The comprehensive agent 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": "最终 Agent 是组合既有层,而不是换掉循环",
"description": "综合 Agent 没有用新架构替换循环,而是把 memory、task、skill、后台任务、团队、worktree、MCP 组合到同一个模型-工具-结果循环周围。"
},
"ja": {
"title": "最終エージェントは既存レイヤーの合成",
"description": "総合エージェントはループを新しい構造で置き換えません。memory、task、skill、バックグラウンド処理、チーム、worktree、MCP を同じ model-tool-result サイクルの周囲に合成します。"
}
},
{
"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": "上下文组装从 memory、task graph、skills、tool registry、policy 等具名来源读取。大型 agent 因此仍可调试,因为每块 prompt context 都有清晰归属。"
},
"ja": {
"title": "ランタイム状態には名前付きの出所がある",
"description": "コンテキスト組み立ては memory、task graph、skills、tool registry、policy などの名前付きソースから取得します。各 prompt context に所有者があるため、大きなエージェントでもデバッグ可能です。"
}
},
{
"id": "recovery-is-first-class",
"title": "Recovery Is Part of the Main Flow",
"description": "Compaction, error recovery, and asynchronous result collection are treated as normal loop behavior. The final lesson shows that production agents spend as much effort recovering and resuming as they do calling tools.",
"alternatives": "Recovery could be left as error handling around the edges, but then the architecture would understate what real long-running agents need.",
"zh": {
"title": "恢复能力是一等流程",
"description": "压缩、错误恢复、异步结果收集都被视为正常循环行为。最终课展示了生产级 agent 在恢复和续跑上投入的工程量,并不低于调用工具本身。"
},
"ja": {
"title": "リカバリは主要フローの一部",
"description": "圧縮、エラー回復、非同期結果収集を通常のループ動作として扱います。実運用の長時間エージェントでは、ツール呼び出しと同じくらい回復と再開が重要であることを示します。"
}
}
]
}

View File

@@ -308,8 +308,497 @@ export const EXECUTION_FLOWS: Record<string, FlowDefinition> = {
{ 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 },
{ 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: "spawn", label: "Spawn Thread", type: "subprocess", x: 70, y: 370 },
{ id: "placeholder", label: "Return Placeholder", type: "process", x: 70, y: 450 },
{ id: "notify", label: "Notification\nQueue", type: "process", x: COL_RIGHT, y: 500 },
{ id: "collect", label: "Collect Results", type: "process", x: COL_RIGHT, y: 590 },
{ id: "exec", label: "Execute Tool", type: "subprocess", x: COL_LEFT + 110, y: 370 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 690 },
{ 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: "spawn", label: "bg" },
{ from: "bg_check", to: "exec", label: "fg" },
{ from: "spawn", to: "placeholder" },
{ from: "spawn", to: "notify", label: "done" },
{ from: "notify", to: "collect" },
{ from: "placeholder", to: "append" },
{ from: "exec", to: "append" },
{ from: "collect", to: "append" },
{ from: "append", to: "llm" },
],
},
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 },
{ id: "tool_check", label: "cron tool?", type: "decision", x: COL_CENTER, y: 190 },
{ id: "schedule", label: "schedule_cron", type: "subprocess", x: COL_LEFT, y: 280 },
{ id: "store", label: "Durable Store\n.scheduled_tasks", type: "process", x: COL_LEFT, y: 360 },
{ id: "scheduler", label: "Scheduler Loop", type: "process", x: COL_CENTER, y: 450 },
{ id: "match", label: "cron_matches?", type: "decision", x: COL_CENTER, y: 540 },
{ id: "queue", label: "Cron Queue", type: "process", x: COL_RIGHT, y: 540 },
{ id: "processor", label: "Queue Processor", type: "process", x: COL_RIGHT, y: 630 },
{ id: "agent", label: "Agent Loop", type: "process", x: COL_CENTER, y: 720 },
{ 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: "schedule", label: "yes" },
{ from: "tool_check", to: "end", label: "no" },
{ from: "schedule", to: "store" },
{ from: "store", to: "scheduler" },
{ from: "scheduler", to: "match" },
{ from: "match", to: "queue", label: "due" },
{ from: "queue", to: "processor" },
{ from: "processor", to: "agent" },
{ from: "agent", to: "llm" },
],
},
s15: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "lead", label: "Lead LLM", type: "process", x: COL_CENTER, y: 110 },
{ id: "team_tool", label: "team tool?", type: "decision", x: COL_CENTER, y: 200 },
{ id: "spawn", label: "Spawn Teammate", type: "subprocess", x: COL_LEFT, y: 300 },
{ id: "send", label: "Send Message", type: "subprocess", x: COL_CENTER, y: 300 },
{ id: "bus", label: "MessageBus\n.mailboxes", type: "process", x: COL_CENTER, y: 400 },
{ id: "teammate", label: "Teammate Loop", type: "process", x: COL_RIGHT, y: 500 },
{ id: "tools", label: "Scoped Tools", type: "subprocess", x: COL_RIGHT, y: 590 },
{ id: "inbox", label: "Lead Inbox", type: "process", x: COL_CENTER, y: 700 },
{ id: "append", label: "Append Result", type: "process", x: COL_LEFT, y: 700 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 300 },
],
edges: [
{ from: "start", to: "lead" },
{ from: "lead", to: "team_tool" },
{ from: "team_tool", to: "spawn", label: "spawn" },
{ from: "team_tool", to: "send", label: "send" },
{ from: "team_tool", to: "end", label: "no" },
{ from: "spawn", to: "bus", label: "register" },
{ from: "send", to: "bus" },
{ from: "bus", to: "teammate" },
{ from: "teammate", to: "tools" },
{ from: "tools", to: "bus", label: "reply" },
{ from: "bus", to: "inbox" },
{ from: "inbox", to: "append" },
{ from: "append", to: "lead" },
],
},
s16: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "lead", label: "Lead LLM", type: "process", x: COL_CENTER, y: 110 },
{ id: "protocol", label: "protocol?", type: "decision", x: COL_CENTER, y: 200 },
{ id: "request", label: "request_plan /\nrequest_shutdown", type: "subprocess", x: COL_LEFT, y: 300 },
{ id: "pending", label: "Pending Requests\nrequest_id", type: "process", x: COL_LEFT, y: 390 },
{ id: "dispatch", label: "Dispatch Message", type: "process", x: COL_CENTER, y: 470 },
{ id: "teammate", label: "Teammate Handler", type: "process", x: COL_RIGHT, y: 470 },
{ id: "response", label: "submit_plan /\nack shutdown", type: "subprocess", x: COL_RIGHT, y: 560 },
{ id: "match", label: "match_response?", type: "decision", x: COL_CENTER, y: 640 },
{ id: "append", label: "Append Protocol\nResult", type: "process", x: COL_CENTER, y: 730 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 300 },
],
edges: [
{ from: "start", to: "lead" },
{ from: "lead", to: "protocol" },
{ from: "protocol", to: "request", label: "yes" },
{ from: "protocol", to: "end", label: "no" },
{ from: "request", to: "pending" },
{ from: "pending", to: "dispatch" },
{ from: "dispatch", to: "teammate" },
{ from: "teammate", to: "response" },
{ from: "response", to: "match" },
{ from: "match", to: "append", label: "matched" },
{ from: "append", to: "lead" },
],
},
s17: {
nodes: [
{ id: "start", label: "System Tick", type: "start", x: COL_CENTER, y: 30 },
{ id: "idle", label: "Idle Poll", type: "process", x: COL_CENTER, y: 110 },
{ id: "scan", label: "Scan Tasks", type: "subprocess", x: COL_CENTER, y: 190 },
{ id: "claimable", label: "claimable?", type: "decision", x: COL_CENTER, y: 280 },
{ id: "claim", label: "claim_task\n(owner check)", type: "subprocess", x: COL_LEFT, y: 380 },
{ id: "work", label: "WORK State", type: "process", x: COL_LEFT, y: 470 },
{ id: "complete", label: "complete_task", type: "subprocess", x: COL_LEFT, y: 560 },
{ id: "inbox", label: "Check Inbox", type: "process", x: COL_RIGHT, y: 380 },
{ id: "shutdown", label: "Shutdown?", type: "decision", x: COL_RIGHT, y: 470 },
{ id: "done", label: "IDLE / SHUTDOWN", type: "end", x: COL_RIGHT, y: 560 },
],
edges: [
{ from: "start", to: "idle" },
{ from: "idle", to: "scan" },
{ from: "scan", to: "claimable" },
{ from: "claimable", to: "claim", label: "yes" },
{ from: "claimable", to: "inbox", label: "no" },
{ from: "claim", to: "work" },
{ from: "work", to: "complete" },
{ from: "complete", to: "idle" },
{ from: "inbox", to: "shutdown" },
{ from: "shutdown", to: "done", label: "yes" },
{ from: "shutdown", to: "idle", label: "no" },
],
},
s18: {
nodes: [
{ id: "start", label: "Task Selected", type: "start", x: COL_CENTER, y: 30 },
{ id: "create", label: "create_worktree", type: "subprocess", x: COL_CENTER, y: 110 },
{ id: "validate", label: "Validate Name", type: "process", x: COL_CENTER, y: 190 },
{ id: "git", label: "git worktree add", type: "subprocess", x: COL_LEFT, y: 290 },
{ id: "bind", label: "Bind Task\nworktree field", type: "process", x: COL_LEFT, y: 380 },
{ id: "run", label: "Run in Isolated\nDirectory", type: "subprocess", x: COL_CENTER, y: 470 },
{ id: "events", label: "Lifecycle Events\n.events.jsonl", type: "process", x: COL_RIGHT, y: 190 },
{ id: "close", label: "keep / remove", type: "decision", x: COL_CENTER, y: 560 },
{ id: "cleanup", label: "remove_worktree", type: "subprocess", x: COL_LEFT, y: 650 },
{ id: "keep", label: "keep_worktree", type: "process", x: COL_RIGHT, y: 650 },
{ id: "end", label: "Task Result", type: "end", x: COL_CENTER, y: 740 },
],
edges: [
{ from: "start", to: "create" },
{ from: "create", to: "validate" },
{ from: "validate", to: "git" },
{ from: "git", to: "bind" },
{ from: "bind", to: "run" },
{ from: "create", to: "events", label: "emit" },
{ from: "run", to: "events", label: "status" },
{ from: "run", to: "close" },
{ from: "close", to: "cleanup", label: "remove" },
{ from: "close", to: "keep", label: "keep" },
{ from: "cleanup", to: "end" },
{ from: "keep", to: "end" },
],
},
s19: {
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: "connect", label: "connect_mcp?", type: "decision", x: COL_CENTER, y: 200 },
{ id: "client", label: "MCP Client", type: "process", x: COL_LEFT, y: 300 },
{ id: "discover", label: "Discover Tools", type: "subprocess", x: COL_LEFT, y: 390 },
{ id: "pool", label: "Assemble Tool Pool\nmcp__server__tool", type: "process", x: COL_CENTER, y: 480 },
{ id: "call", label: "MCP Tool Call", type: "subprocess", x: COL_RIGHT, y: 390 },
{ id: "server", label: "External Server", type: "process", x: COL_RIGHT, y: 480 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 580 },
{ id: "end", label: "Output", type: "end", x: 520, y: 200 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "connect" },
{ from: "connect", to: "client", label: "connect" },
{ from: "connect", to: "call", label: "use" },
{ from: "connect", to: "end", label: "done" },
{ from: "client", to: "discover" },
{ from: "discover", to: "pool" },
{ from: "pool", to: "llm" },
{ from: "call", to: "server" },
{ from: "server", to: "append" },
{ from: "append", to: "llm" },
],
},
s20: {
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 },
{ id: "policy", label: "Policy + Hooks", type: "process", x: COL_LEFT, y: 210 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 300 },
{ id: "router", label: "Route Tool", type: "decision", x: COL_CENTER, y: 390 },
{ id: "builtin", label: "Built-in Tools", type: "subprocess", x: 85, y: 500 },
{ id: "team", label: "Teams /\nProtocols", type: "subprocess", x: 230, y: 500 },
{ id: "async", label: "Background /\nCron", type: "subprocess", x: 370, y: 500 },
{ id: "external", label: "Worktree /\nMCP", type: "subprocess", x: 515, y: 500 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 610 },
{ id: "recover", label: "Recover /\nCompact", type: "process", x: COL_LEFT, y: 700 },
{ id: "end", label: "Final Output", type: "end", x: COL_RIGHT, y: 390 },
],
edges: [
{ from: "start", to: "context" },
{ from: "context", to: "policy" },
{ from: "policy", to: "llm" },
{ from: "context", to: "llm" },
{ from: "llm", to: "router" },
{ from: "router", to: "builtin", label: "local" },
{ from: "router", to: "team", label: "team" },
{ from: "router", to: "async", label: "async" },
{ from: "router", to: "external", label: "ext" },
{ from: "router", to: "end", label: "done" },
{ from: "builtin", to: "append" },
{ from: "team", to: "append" },
{ from: "async", to: "append" },
{ from: "external", to: "append" },
{ from: "append", to: "recover" },
{ from: "recover", to: "context" },
],
},
};
const CURRENT_FLOW_OVERRIDES: Record<string, FlowDefinition> = {
s03: {
nodes: [
{ id: "start", label: "Tool Call", type: "start", x: COL_CENTER, y: 30 },
{ id: "hard", label: "Hard Deny?", type: "decision", x: COL_CENTER, y: 120 },
{ id: "rules", label: "Rule Match?", type: "decision", x: COL_CENTER, y: 220 },
{ id: "ask", label: "Ask User", type: "subprocess", x: COL_LEFT, y: 320 },
{ id: "allow", label: "Approved?", type: "decision", x: COL_LEFT, y: 410 },
{ id: "exec", label: "Execute Tool", type: "process", x: COL_CENTER, y: 520 },
{ id: "blocked_policy", label: "Blocked", type: "end", x: COL_RIGHT, y: 120 },
{ id: "blocked_user", label: "Blocked", type: "end", x: COL_RIGHT, y: 410 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 610 },
],
edges: [
{ from: "start", to: "hard" },
{ from: "hard", to: "blocked_policy", label: "deny" },
{ from: "hard", to: "rules", label: "ok" },
{ from: "rules", to: "ask", label: "needs approval" },
{ from: "rules", to: "exec", label: "allow" },
{ from: "ask", to: "allow" },
{ from: "allow", to: "blocked_user", label: "no" },
{ from: "allow", to: "exec", label: "yes" },
{ from: "exec", to: "append" },
],
},
s04: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "user_hook", label: "UserPromptSubmit\nHooks", type: "subprocess", x: COL_CENTER, y: 120 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 220 },
{ id: "tool_check", label: "tool_use?", type: "decision", x: COL_CENTER, y: 310 },
{ id: "pre", label: "PreToolUse\nHooks", type: "subprocess", x: COL_LEFT, y: 410 },
{ id: "blocked", label: "Blocked?", type: "decision", x: COL_LEFT, y: 500 },
{ id: "exec", label: "Tool Handler", type: "process", x: COL_CENTER, y: 600 },
{ id: "post", label: "PostToolUse\nHooks", type: "subprocess", x: COL_CENTER, y: 690 },
{ id: "stop", label: "Stop Hooks", type: "subprocess", x: COL_RIGHT, y: 410 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 500 },
],
edges: [
{ from: "start", to: "user_hook" },
{ from: "user_hook", to: "llm" },
{ from: "llm", to: "tool_check" },
{ from: "tool_check", to: "pre", label: "yes" },
{ from: "tool_check", to: "stop", label: "no" },
{ from: "pre", to: "blocked" },
{ from: "blocked", to: "end", label: "yes" },
{ from: "blocked", to: "exec", label: "no" },
{ from: "exec", to: "post" },
{ from: "post", to: "llm" },
{ from: "stop", to: "end" },
],
},
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: 120 },
{ id: "tool", label: "tool_use?", type: "decision", x: COL_CENTER, y: 210 },
{ id: "todo", label: "todo_write?", type: "decision", x: COL_LEFT, y: 310 },
{ id: "update", label: "Update\ncurrent_todos", type: "process", x: COL_LEFT, y: 410 },
{ id: "other", label: "Run Tool", type: "subprocess", x: COL_CENTER, y: 410 },
{ id: "reminder", label: "3 rounds?\nInject Reminder", type: "process", x: COL_RIGHT, y: 500 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 590 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 310 },
],
edges: [
{ from: "start", to: "llm" },
{ from: "llm", to: "tool" },
{ from: "tool", to: "todo", label: "yes" },
{ from: "tool", to: "end", label: "no" },
{ from: "todo", to: "update", label: "todo" },
{ from: "todo", to: "other", label: "other" },
{ from: "update", to: "append" },
{ from: "other", to: "append" },
{ from: "append", to: "reminder" },
{ from: "reminder", to: "llm" },
],
},
s06: {
nodes: [
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "parent", label: "Parent LLM", type: "process", x: COL_CENTER, y: 120 },
{ id: "task_check", label: "task tool?", type: "decision", x: COL_CENTER, y: 220 },
{ id: "spawn", label: "Spawn Subagent\nfresh messages[]", type: "subprocess", x: COL_LEFT, y: 330 },
{ id: "subloop", label: "Subagent Loop\nmax 30 turns", type: "process", x: COL_LEFT, y: 430 },
{ id: "summary", label: "Return Summary\nOnly", type: "process", x: COL_LEFT, y: 530 },
{ id: "tool", label: "Run Parent Tool", type: "subprocess", x: COL_RIGHT, y: 330 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 630 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 220 },
],
edges: [
{ from: "start", to: "parent" },
{ from: "parent", to: "task_check" },
{ from: "task_check", to: "spawn", label: "task" },
{ from: "task_check", to: "tool", label: "other" },
{ from: "task_check", to: "end", label: "done" },
{ from: "spawn", to: "subloop" },
{ from: "subloop", to: "summary" },
{ from: "summary", to: "append" },
{ from: "tool", to: "append" },
{ from: "append", to: "parent" },
],
},
s07: {
nodes: [
{ id: "start", label: "Startup", type: "start", x: COL_CENTER, y: 30 },
{ id: "scan", label: "Scan skills/", type: "process", x: COL_CENTER, y: 120 },
{ id: "catalog", label: "Inject Catalog\nOnly", type: "process", x: COL_CENTER, y: 210 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 310 },
{ id: "need", label: "load_skill?", type: "decision", x: COL_CENTER, y: 400 },
{ id: "read", label: "Read SKILL.md", type: "subprocess", x: COL_LEFT, y: 500 },
{ id: "inject", label: "Tool Result\nFull Skill", type: "process", x: COL_LEFT, y: 590 },
{ id: "other", label: "Other Tool", type: "subprocess", x: COL_RIGHT, y: 500 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 690 },
],
edges: [
{ from: "start", to: "scan" },
{ from: "scan", to: "catalog" },
{ from: "catalog", to: "llm" },
{ from: "llm", to: "need" },
{ from: "need", to: "read", label: "yes" },
{ from: "need", to: "other", label: "no" },
{ from: "read", to: "inject" },
{ from: "inject", to: "append" },
{ from: "other", to: "append" },
{ from: "append", to: "llm" },
],
},
s08: {
nodes: [
{ id: "start", label: "messages[]", type: "start", x: COL_CENTER, y: 30 },
{ id: "budget", label: "Tool Result\nBudget", type: "process", x: COL_CENTER, y: 120 },
{ id: "snip", label: "Snip Compact", type: "process", x: COL_CENTER, y: 210 },
{ id: "micro", label: "Micro Compact", type: "process", x: COL_CENTER, y: 300 },
{ id: "threshold", label: "over limit?", type: "decision", x: COL_CENTER, y: 390 },
{ id: "summary", label: "LLM Summary\nCompact", type: "subprocess", x: COL_LEFT, y: 500 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 610 },
{ id: "too_long", label: "prompt_too_long?", type: "decision", x: COL_RIGHT, y: 610 },
{ id: "reactive", label: "Reactive Compact", type: "subprocess", x: COL_LEFT, y: 720 },
],
edges: [
{ from: "start", to: "budget" },
{ from: "budget", to: "snip" },
{ from: "snip", to: "micro" },
{ from: "micro", to: "threshold" },
{ from: "threshold", to: "summary", label: "yes" },
{ from: "threshold", to: "llm", label: "no" },
{ from: "summary", to: "llm" },
{ from: "llm", to: "too_long" },
{ from: "too_long", to: "reactive", label: "yes" },
{ from: "reactive", to: "llm" },
],
},
s09: {
nodes: [
{ id: "start", label: "Session Start", type: "start", x: COL_CENTER, y: 30 },
{ id: "index", label: "Load MEMORY.md\nIndex", type: "process", x: COL_CENTER, y: 120 },
{ id: "select", label: "Select Relevant\nMemory Files", type: "process", x: COL_CENTER, y: 220 },
{ id: "inject", label: "Inject Memory\nContent", type: "process", x: COL_CENTER, y: 320 },
{ id: "compact", label: "Compact Pipeline", type: "subprocess", x: COL_CENTER, y: 420 },
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 520 },
{ id: "extract", label: "Extract New\nMemories", type: "subprocess", x: COL_LEFT, y: 630 },
{ id: "write", label: "Write .memory\nFiles", type: "process", x: COL_LEFT, y: 720 },
{ id: "dream", label: "Periodic\nConsolidate", type: "process", x: COL_RIGHT, y: 720 },
],
edges: [
{ from: "start", to: "index" },
{ from: "index", to: "select" },
{ from: "select", to: "inject" },
{ from: "inject", to: "compact" },
{ from: "compact", to: "llm" },
{ from: "llm", to: "extract" },
{ from: "extract", to: "write" },
{ from: "write", to: "index" },
{ from: "write", to: "dream" },
],
},
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 },
{ id: "save", label: "Persist JSON\n.tasks/", type: "process", x: COL_CENTER, y: 210 },
{ id: "list", label: "list / get", type: "subprocess", x: COL_RIGHT, y: 300 },
{ id: "deps", label: "blockedBy\ncomplete?", type: "decision", x: COL_CENTER, y: 390 },
{ id: "blocked", label: "Remain Pending", type: "end", x: COL_RIGHT, y: 490 },
{ id: "claim", label: "claim_task\nowner + in_progress", type: "subprocess", x: COL_LEFT, y: 490 },
{ id: "complete", label: "complete_task", type: "subprocess", x: COL_LEFT, y: 590 },
{ id: "unblock", label: "Report\nUnblocked", type: "process", x: COL_CENTER, y: 690 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 780 },
],
edges: [
{ from: "start", to: "create" },
{ from: "create", to: "save" },
{ from: "save", to: "list" },
{ from: "save", to: "deps" },
{ from: "deps", to: "blocked", label: "no" },
{ from: "deps", to: "claim", label: "yes" },
{ from: "claim", to: "complete" },
{ from: "complete", to: "unblock" },
{ from: "unblock", to: "append" },
{ from: "append", to: "list" },
],
},
};
export function getFlowForVersion(version: string): FlowDefinition | null {
return EXECUTION_FLOWS[version] ?? null;
return CURRENT_FLOW_OVERRIDES[version] ?? EXECUTION_FLOWS[version] ?? null;
}

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,6 +1,6 @@
{
"version": "s02",
"title": "Tools",
"title": "Tool Use",
"description": "Agent with read, write, edit, and bash tools",
"steps": [
{

View File

@@ -1,53 +1,39 @@
{
"version": "s03",
"title": "TodoWrite",
"description": "Agent creates a visible plan before executing",
"title": "Permission",
"description": "Tool calls pass through hard denies, rule checks, and optional user approval before execution.",
"steps": [
{
"type": "user_message",
"content": "Build a CLI calculator that supports add, subtract, multiply, divide",
"annotation": "A multi-step task that benefits from planning"
},
{
"type": "tool_call",
"content": "[\n { \"id\": 1, \"task\": \"Create calc.py with argument parsing\", \"status\": \"in_progress\" },\n { \"id\": 2, \"task\": \"Implement 4 operations\", \"status\": \"pending\" },\n { \"id\": 3, \"task\": \"Add error handling\", \"status\": \"pending\" },\n { \"id\": 4, \"task\": \"Test all operations\", \"status\": \"pending\" }\n]",
"toolName": "todo_write",
"annotation": "TodoManager creates a visible plan BEFORE any code is written"
},
{
"type": "tool_result",
"content": "Todo list created with 4 tasks",
"toolName": "todo_write",
"annotation": "The plan is now visible and trackable"
},
{
"type": "tool_call",
"content": "calc.py\n---\nimport sys\n\ndef calculate(op, a, b):\n ops = {'+': a+b, '-': a-b, '*': a*b, '/': a/b}\n return ops.get(op, 'Unknown operator')\n\nif __name__ == '__main__':\n a, op, b = float(sys.argv[1]), sys.argv[2], float(sys.argv[3])\n print(calculate(op, a, b))",
"toolName": "write_file",
"annotation": "Executing task #1 and #2: create file with operations"
},
{
"type": "tool_result",
"content": "File written successfully",
"toolName": "write_file",
"annotation": "Code written"
},
{
"type": "tool_call",
"content": "python calc.py 10 + 5",
"toolName": "bash",
"annotation": "Executing task #4: testing"
},
{
"type": "tool_result",
"content": "15.0",
"toolName": "bash",
"annotation": "Test passes"
"content": "Delete the temporary build directory.",
"annotation": "The user asks for a filesystem action that may be safe or dangerous depending on the path."
},
{
"type": "assistant_text",
"content": "Calculator built and tested. All 4 operations work correctly.",
"annotation": "Visible plans improve task completion by making progress trackable"
"content": "I'll check the target before running the command.",
"annotation": "The model can propose a tool call, but the harness decides whether execution is allowed."
},
{
"type": "tool_call",
"toolName": "bash",
"content": "rm -rf /tmp/build-cache",
"annotation": "A destructive-looking command enters the permission pipeline."
},
{
"type": "system_event",
"content": "Gate 1 hard deny: pass\nGate 2 rule match: destructive command -> ask user",
"annotation": "The command is not absolutely forbidden, but it requires confirmation."
},
{
"type": "system_event",
"content": "User approval: allowed",
"annotation": "The runtime pauses before execution and resumes only after approval."
},
{
"type": "tool_result",
"toolName": "bash",
"content": "(no output)",
"annotation": "Allowed commands continue through the normal tool result path."
}
]
}

View File

@@ -1,51 +1,44 @@
{
"version": "s04",
"title": "Subagents",
"description": "Agent spawns a subagent for isolated subtask execution",
"title": "Hooks",
"description": "Lifecycle hooks attach logging, permission, and output handling around the same stable agent loop.",
"steps": [
{
"type": "user_message",
"content": "Refactor the calculator: split into modules and add tests",
"annotation": "Complex task suitable for delegation"
},
{
"type": "assistant_text",
"content": "I'll delegate the test writing to a subagent while I handle the refactoring.",
"annotation": "Agent decides to split work using the Task tool"
},
{
"type": "tool_call",
"content": "Write unit tests for calc.py covering all 4 operations and edge cases like division by zero",
"toolName": "task",
"annotation": "Task tool spawns a subagent with its own isolated context"
"content": "Read package.json and summarize the scripts.",
"annotation": "UserPromptSubmit hooks run before the prompt enters the LLM call."
},
{
"type": "system_event",
"content": "[Subagent spawned] Context: fresh messages[], same tools, scoped prompt",
"annotation": "Subagent gets clean context -- no confusion from parent's conversation"
},
{
"type": "tool_result",
"content": "Created test_calc.py with 8 test cases. All passing.",
"toolName": "task",
"annotation": "Subagent completes and returns a summary to parent"
"content": "trigger_hooks(UserPromptSubmit): log_user_prompt",
"annotation": "Extension logic is outside the core loop."
},
{
"type": "tool_call",
"content": "calc.py -> calc/operations.py, calc/cli.py",
"toolName": "write_file",
"annotation": "Parent continues with its own task: refactoring"
"toolName": "read_file",
"content": "{\"path\":\"package.json\"}",
"annotation": "The LLM asks to use a tool."
},
{
"type": "system_event",
"content": "trigger_hooks(PreToolUse): permission_hook, log_hook -> allowed",
"annotation": "PreToolUse hooks can block, audit, or annotate the tool call."
},
{
"type": "tool_result",
"content": "Files written successfully",
"toolName": "write_file",
"annotation": "Modules split"
"toolName": "read_file",
"content": "{ \"scripts\": { \"build\": \"next build\" } }",
"annotation": "The handler executes only after hooks allow it."
},
{
"type": "system_event",
"content": "trigger_hooks(PostToolUse): large_output_hook",
"annotation": "PostToolUse hooks see the result before it returns to the model."
},
{
"type": "assistant_text",
"content": "Refactored into calc/operations.py and calc/cli.py. Tests were written by a subagent and are all passing.",
"annotation": "Context isolation prevents confusion between tasks"
"content": "The project exposes a build script that runs Next's production build.",
"annotation": "Stop hooks can run when the model no longer requests tools."
}
]
}

View File

@@ -1,44 +1,40 @@
{
"version": "s05",
"title": "Skills",
"description": "Agent loads external knowledge from SKILL.md files",
"title": "TodoWrite",
"description": "A planning tool makes the agent's intended work visible and keeps long tasks from drifting.",
"steps": [
{
"type": "user_message",
"content": "Set up a new Next.js project with TypeScript",
"annotation": "Task requires framework-specific knowledge"
},
{
"type": "system_event",
"content": "[SkillLoader] Scanning for SKILL.md files... Found: nextjs.SKILL.md",
"annotation": "SkillLoader finds a matching skill file before the loop runs"
},
{
"type": "system_event",
"content": "[Skill injected] nextjs.SKILL.md -> tool_result message appended",
"annotation": "Skill content is injected as a tool_result, not bloating the system prompt"
},
{
"type": "assistant_text",
"content": "I have knowledge about Next.js setup. Let me create the project structure.",
"annotation": "Model now has framework-specific knowledge from the skill file"
"content": "Refactor the parser and add a regression test.",
"annotation": "The task has multiple steps, so planning should become visible."
},
{
"type": "tool_call",
"content": "npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir",
"toolName": "bash",
"annotation": "Using skill-provided knowledge for correct flags"
"toolName": "todo_write",
"content": "[{\"content\":\"Inspect parser\",\"status\":\"in_progress\"},{\"content\":\"Refactor parsing branch\",\"status\":\"pending\"},{\"content\":\"Add regression test\",\"status\":\"pending\"}]",
"annotation": "The model records a plan before acting."
},
{
"type": "tool_result",
"content": "Success! Created my-app at /workspace/my-app",
"toolName": "bash",
"annotation": "Project created with correct configuration"
"toolName": "todo_write",
"content": "Todos updated.",
"annotation": "The current todo list is kept by the harness."
},
{
"type": "tool_call",
"toolName": "read_file",
"content": "{\"path\":\"parser.py\"}",
"annotation": "Normal tools continue through the same dispatch table."
},
{
"type": "system_event",
"content": "<reminder>Update todo status after 3 rounds without todo_write.</reminder>",
"annotation": "The nag reminder keeps the plan fresh during long work."
},
{
"type": "assistant_text",
"content": "Next.js project created with TypeScript, Tailwind CSS, ESLint, App Router, and src/ directory.",
"annotation": "Skills inject via tool_result, not system prompt -- keeping context lean"
"content": "I inspected the parser and will update the todo list before making the code change.",
"annotation": "The planning state guides the next turn."
}
]
}

View File

@@ -1,51 +1,40 @@
{
"version": "s06",
"title": "Compact",
"description": "Agent compresses context when tokens exceed threshold",
"title": "Subagent",
"description": "The task tool spawns a fresh subagent context and returns only a final summary to the parent.",
"steps": [
{
"type": "user_message",
"content": "Analyze all Python files in this project and summarize each one",
"annotation": "A task that will generate large amounts of context"
"content": "Explore how the CLI parser works without polluting this main context.",
"annotation": "The user asks for exploratory work that can be isolated."
},
{
"type": "tool_call",
"content": "find . -name '*.py' -type f",
"toolName": "bash",
"annotation": "Finding all files to analyze"
},
{
"type": "tool_result",
"content": "./src/main.py\n./src/utils.py\n./src/models.py\n./tests/test_main.py\n... (28 more files)",
"toolName": "bash",
"annotation": "32 files found -- reading all of them will generate massive context"
},
{
"type": "assistant_text",
"content": "I'll read and analyze each file systematically.",
"annotation": "Agent begins processing files one by one"
},
{
"type": "tool_call",
"content": "./src/main.py",
"toolName": "read_file",
"annotation": "Reading file 1 of 32..."
},
{
"type": "tool_result",
"content": "[... 450 lines of code ...]",
"toolName": "read_file",
"annotation": "Large file content added to messages[]"
"toolName": "task",
"content": "{\"prompt\":\"Explore the CLI parser and summarize the control flow.\"}",
"annotation": "The parent delegates through the task tool."
},
{
"type": "system_event",
"content": "[ContextManager] Token count: 148,231 / 150,000 threshold\nTriggering 3-layer compression:\n 1. Drop tool_result content > 1000 chars -> replaced with summary\n 2. Merge sequential assistant turns\n 3. Summarize oldest 50% of conversation\nResult: 148,231 -> 42,087 tokens",
"annotation": "Compression fires automatically when approaching the limit"
"content": "spawn_subagent: messages=[{role:\"user\", content: prompt}], tools=bash/read/write/edit/glob",
"annotation": "The child receives a fresh message history and no recursive task tool."
},
{
"type": "tool_call",
"toolName": "read_file",
"content": "{\"path\":\"cli.py\"}",
"annotation": "Inside the child loop, intermediate tool calls stay private."
},
{
"type": "tool_result",
"toolName": "task",
"content": "Summary: cli.py parses args, selects a command handler, then dispatches.",
"annotation": "Only the subagent's final summary returns to the parent."
},
{
"type": "assistant_text",
"content": "Continuing analysis with compressed context. Files 1-18 have been summarized, proceeding with remaining files.",
"annotation": "Forgetting old tool results enables infinite work -- the agent can process any number of files"
"content": "The parser is isolated in cli.py and dispatches by command name. I'll use that summary in the main plan.",
"annotation": "The parent avoids carrying every exploratory detail."
}
]
}

View File

@@ -1,53 +1,34 @@
{
"version": "s07",
"title": "Tasks",
"description": "Agent uses persistent file-based tasks with dependencies",
"title": "Skills",
"description": "The agent sees a cheap skill catalog first, then loads full SKILL.md content only when needed.",
"steps": [
{
"type": "system_event",
"content": "build_system: skills available: agent-builder, code-review, mcp-builder, pdf",
"annotation": "Startup scans the skills directory and injects short descriptions into the system prompt."
},
{
"type": "user_message",
"content": "Build a REST API with user auth, database, and deployment config",
"annotation": "Multi-part task requiring dependency tracking"
"content": "Review this change like a code reviewer.",
"annotation": "The user asks for specialized behavior."
},
{
"type": "tool_call",
"content": "create_task(\"Set up database schema\", deps=[])\ncreate_task(\"Implement user model\", deps=[1])\ncreate_task(\"Add auth endpoints\", deps=[2])\ncreate_task(\"Write deployment config\", deps=[3])",
"toolName": "task_manager",
"annotation": "TaskManager creates file-based tasks with explicit dependencies"
"toolName": "load_skill",
"content": "{\"name\":\"code-review\"}",
"annotation": "The model chooses to load the full skill on demand."
},
{
"type": "tool_result",
"content": "Created 4 tasks in .tasks/\n #1 Set up database schema [ready]\n #2 Implement user model [blocked by #1]\n #3 Add auth endpoints [blocked by #2]\n #4 Write deployment config [blocked by #3]",
"toolName": "task_manager",
"annotation": "Tasks persisted to .tasks/ directory as JSON files"
},
{
"type": "tool_call",
"content": "schema.sql\n---\nCREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT UNIQUE, password_hash TEXT);",
"toolName": "write_file",
"annotation": "Working on task #1"
},
{
"type": "tool_result",
"content": "File written successfully",
"toolName": "write_file",
"annotation": "Schema created"
},
{
"type": "tool_call",
"content": "complete_task(1)",
"toolName": "task_manager",
"annotation": "Completing task #1 automatically unblocks task #2"
},
{
"type": "tool_result",
"content": "Task #1 completed. Task #2 is now ready.",
"toolName": "task_manager",
"annotation": "File-based persistence outlives process memory"
"toolName": "load_skill",
"content": "# Code Review Skill\nFocus on bugs, regressions, and missing tests...",
"annotation": "Full skill text is injected as a tool result, not permanently in the system prompt."
},
{
"type": "assistant_text",
"content": "Database schema is done. Moving to user model implementation.",
"annotation": "Dependency graph ensures tasks execute in correct order"
"content": "I'll review the diff for behavioral risks first, then note test gaps.",
"annotation": "The loaded skill guides the next response."
}
]
}

View File

@@ -1,56 +1,33 @@
{
"version": "s08",
"title": "Background Tasks",
"description": "Agent executes long-running tasks in background threads",
"title": "Context Compact",
"description": "A layered compaction pipeline trims cheap context first and calls the LLM summary only when needed.",
"steps": [
{
"type": "user_message",
"content": "Run the test suite and while it's running, fix the linting errors",
"annotation": "Two tasks that can run concurrently"
},
{
"type": "assistant_text",
"content": "I'll start the tests in the background and fix linting in parallel.",
"annotation": "Agent recognizes opportunity for concurrent execution"
},
{
"type": "tool_call",
"content": "pytest tests/ -v --timeout=300",
"toolName": "background_task",
"annotation": "BackgroundManager spawns a thread for long-running test suite"
"type": "system_event",
"content": "messages: 72 entries, tool_result output: 180k chars",
"annotation": "The context window is approaching the budget."
},
{
"type": "system_event",
"content": "[BackgroundManager] Thread spawned: task_001\nRunning: pytest tests/ -v --timeout=300\nStatus: in_progress",
"annotation": "Thread is running independently -- main loop continues"
},
{
"type": "tool_result",
"content": "Background task started: task_001",
"toolName": "background_task",
"annotation": "Control returns immediately to the agent"
},
{
"type": "tool_call",
"content": "ruff check --fix .",
"toolName": "bash",
"annotation": "Agent works on linting while tests run in background"
},
{
"type": "tool_result",
"content": "Fixed 12 linting errors in 5 files",
"toolName": "bash",
"annotation": "Linting fixed concurrently with tests"
"content": "tool_result_budget -> persisted large output to .large_outputs/out_001.txt",
"annotation": "Large tool outputs are moved out before expensive summarization."
},
{
"type": "system_event",
"content": "[Notification] Background task_001 completed\nResult: 47 tests passed, 2 failed\nFailed: test_auth.py::test_token_expiry, test_api.py::test_rate_limit",
"annotation": "Background thread notifies main loop via notification queue"
"content": "snip_compact -> trimmed middle messages\nmicro_compact -> replaced old tool_results with placeholders",
"annotation": "Cheap compaction layers run every turn."
},
{
"type": "assistant_text",
"content": "Linting is fixed. Tests finished: 47 passed, 2 failed. I'll fix the failing tests now.",
"annotation": "Non-blocking execution via threads + queue enables parallel work"
"type": "system_event",
"content": "token estimate still above threshold -> compact_history()",
"annotation": "Only after cheap passes fail does the runtime ask the LLM for a summary."
},
{
"type": "tool_result",
"toolName": "compact",
"content": "Summary: user asked for parser refactor; files inspected: cli.py, parser.py; open task: add test.",
"annotation": "The compacted summary replaces older context while preserving task-relevant facts."
}
]
}

View File

@@ -1,44 +1,33 @@
{
"version": "s09",
"title": "Agent Teams",
"description": "Persistent teammates communicate via JSONL inboxes",
"title": "Memory",
"description": "Persistent memory keeps selected project facts available across turns and sessions.",
"steps": [
{
"type": "system_event",
"content": "Loaded .memory/MEMORY.md index with 3 entries.",
"annotation": "A cheap index is always available to the system prompt."
},
{
"type": "user_message",
"content": "Build a full-stack app: frontend in React, backend in Python, with tests",
"annotation": "Large task requiring team collaboration"
"content": "Use the repository's preferred test command.",
"annotation": "The user references a fact that may have been learned earlier."
},
{
"type": "system_event",
"content": "Selected memory: project_facts.md -> preferred_test_command=npm run test:unit",
"annotation": "Relevant memory content is injected selectively."
},
{
"type": "tool_call",
"content": "spawn_teammate(\n name: \"frontend-dev\",\n role: \"React specialist\"\n)\nspawn_teammate(\n name: \"backend-dev\",\n role: \"Python API specialist\"\n)",
"toolName": "teammate_manager",
"annotation": "Unlike subagents (s04) that die after one task, teammates persist"
"toolName": "bash",
"content": "npm run test:unit",
"annotation": "The agent acts on durable knowledge without asking again."
},
{
"type": "system_event",
"content": "[TeammateManager] Team created:\n .team/config.json written\n frontend-dev [idle] - own thread, own agent loop\n backend-dev [idle] - own thread, own agent loop",
"annotation": "Each teammate runs its own while(tool_use) loop in a separate thread"
},
{
"type": "tool_call",
"content": "send_message(\n to: \"backend-dev\",\n content: \"Build the user API: GET/POST /users\"\n)",
"toolName": "send_message",
"annotation": "Message appended to .team/inbox/backend-dev.jsonl"
},
{
"type": "system_event",
"content": "[backend-dev] Checking inbox... 1 new message\n From: lead\n Content: \"Build the user API: GET/POST /users\"\n (inbox drained after read)",
"annotation": "Inbox is read-then-clear: prevents duplicate processing"
},
{
"type": "system_event",
"content": "[backend-dev -> frontend-dev] Reply via inbox:\n \"API ready: GET /users, POST /users, GET /users/:id\"\n Written to .team/inbox/frontend-dev.jsonl",
"annotation": "Teammates message each other through the same JSONL mechanism"
},
{
"type": "assistant_text",
"content": "Team is communicating via file-based inboxes. Each teammate has its own .jsonl file -- append to send, drain to read.",
"annotation": "JSONL inboxes decouple sender timing from receiver timing"
"content": "extract_memory -> wrote feedback_tabs.md",
"annotation": "After the turn, new durable facts can be extracted from the original conversation."
}
]
}

View File

@@ -1,38 +1,27 @@
{
"version": "s10",
"title": "Team Protocols",
"description": "Request-response protocols for shutdown and plan approval",
"title": "System Prompt",
"description": "The system prompt is assembled from runtime state and cached by a deterministic context key.",
"steps": [
{
"type": "user_message",
"content": "Work is done. Shut down the team gracefully.",
"annotation": "Can't just kill threads -- need coordinated shutdown"
},
{
"type": "tool_call",
"content": "shutdown_request(\n teammate: \"backend-dev\",\n request_id: \"req_abc123\"\n)",
"toolName": "shutdown_teammate",
"annotation": "request_id correlates the request with the response"
"type": "system_event",
"content": "context={workspace:\"/repo\", memories:true, skills:[\"code-review\"], tools:[\"bash\",\"read_file\"]}",
"annotation": "Prompt inputs are explicit runtime data."
},
{
"type": "system_event",
"content": "[Shutdown FSM] req_abc123: pending\n Lead -> backend-dev inbox:\n { type: \"shutdown_request\", request_id: \"req_abc123\" }",
"annotation": "FSM tracks state: pending -> approved | rejected"
},
{
"type": "system_event",
"content": "[backend-dev] Received shutdown request req_abc123\n Current task: none (idle)\n Decision: approve\n -> lead inbox: { type: \"shutdown_response\", request_id: \"req_abc123\", approve: true }",
"annotation": "Teammate decides whether to approve based on its own state"
},
{
"type": "system_event",
"content": "[Shutdown FSM] req_abc123: approved\n backend-dev thread stopped\n Status: idle -> shutdown",
"annotation": "Same request_id pattern works for plan approval too"
"content": "cache miss -> assemble_system_prompt(context)",
"annotation": "A new context key causes sections to be selected and joined."
},
{
"type": "assistant_text",
"content": "backend-dev shut down gracefully. The same request_id + FSM pattern handles plan approval: teammate submits plan, lead approves/rejects with the correlated request_id.",
"annotation": "One pattern (request_id correlation), two applications (shutdown + plan approval)"
"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": "system_event",
"content": "same context key -> reuse cached prompt",
"annotation": "Repeated turns avoid rebuilding identical prompt text."
}
]
}

View File

@@ -1,44 +1,33 @@
{
"version": "s11",
"title": "Autonomous Agents",
"description": "Teammates self-govern with idle cycles and auto-claiming",
"title": "Error Recovery",
"description": "LLM calls are wrapped with targeted recovery paths for token limits, prompt overflow, and transient provider errors.",
"steps": [
{
"type": "user_message",
"content": "Implement the full feature backlog",
"annotation": "Open-ended task for autonomous team execution"
"type": "system_event",
"content": "try LLM call with max_tokens=8000",
"annotation": "The normal path is still a regular model call."
},
{
"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": "tool_call",
"content": "create_tasks([\n { title: \"User authentication\", status: \"open\" },\n { title: \"Dashboard UI\", status: \"open\" },\n { title: \"API rate limiting\", status: \"open\" },\n { title: \"Integration tests\", status: \"open\" }\n])",
"toolName": "task_manager",
"annotation": "Tasks created with no owner -- available for auto-claiming"
},
{
"type": "tool_result",
"content": "4 tasks created on shared board",
"toolName": "task_manager",
"annotation": "Unassigned tasks visible to all teammates"
},
{
"type": "system_event",
"content": "[frontend-dev] Idle cycle triggered\n 1. Check inbox -> 0 messages\n 2. Poll task board -> found unowned task #2 \"Dashboard UI\"\n 3. Auto-claim task #2\n 4. Status: idle -> working",
"annotation": "Idle cycle: check inbox, poll tasks, auto-claim, resume work"
},
{
"type": "system_event",
"content": "[backend-dev] Idle cycle triggered\n 1. Check inbox -> 0 messages\n 2. Poll task board -> found unowned task #1 \"User authentication\"\n 3. Auto-claim task #1\n 4. Status: idle -> working",
"annotation": "Multiple teammates claim different tasks concurrently"
},
{
"type": "system_event",
"content": "[tester] Idle cycle triggered\n 1. Check inbox -> 0 messages\n 2. Poll task board -> task #4 blocked by #1, #2, #3\n 3. No claimable tasks\n 4. Status: idle (will retry in 30s)",
"annotation": "Timeout-based polling prevents busy-waiting"
},
{
"type": "assistant_text",
"content": "Team is self-organizing: frontend-dev claimed Dashboard UI, backend-dev claimed User auth. Tester is waiting for dependencies to clear.",
"annotation": "Polling + timeout makes teammates autonomous -- no micromanagement needed"
"toolName": "read_file",
"content": "{\"path\":\"README.md\"}",
"annotation": "After recovery succeeds, the agent continues the normal tool loop."
}
]
}

View File

@@ -1,51 +1,48 @@
{
"version": "s12",
"title": "Worktree + Task Isolation",
"description": "Use a shared task board with optional worktree lanes for clean parallel execution",
"title": "Task System",
"description": "A file-persisted task graph tracks status, ownership, and blockedBy dependencies.",
"steps": [
{
"type": "user_message",
"content": "Implement auth refactor and login UI updates in parallel",
"annotation": "Two active tasks in one workspace would collide"
"content": "Break the release into tasks and block deployment until tests pass.",
"annotation": "The user asks for durable multi-step coordination."
},
{
"type": "tool_call",
"content": "task_create(subject: \"Auth refactor\")\ntask_create(subject: \"Login UI polish\")",
"toolName": "task_manager",
"annotation": "Shared board remains the coordination source of truth"
"toolName": "create_task",
"content": "{\"subject\":\"Run tests\"}",
"annotation": "Tasks are created as JSON files under .tasks/."
},
{
"type": "tool_call",
"content": "worktree_create(name: \"auth-refactor\", task_id: 1)\nworktree_create(name: \"ui-login\")\ntask_bind_worktree(task_id: 2, worktree: \"ui-login\")",
"toolName": "worktree_manager",
"annotation": "Lane allocation and task association are composable; task 2 binds after lane creation"
},
{
"type": "system_event",
"content": "worktree.create.before/after emitted\n.tasks/task_1.json -> { status: \"in_progress\", worktree: \"auth-refactor\" }\n.tasks/task_2.json -> { status: \"in_progress\", worktree: \"ui-login\" }\n.worktrees/index.json updated",
"annotation": "Control-plane state remains canonical; hook-style consumers can react to lifecycle events without owning canonical state writes"
"toolName": "create_task",
"content": "{\"subject\":\"Deploy release\",\"blockedBy\":[\"task_tests\"]}",
"annotation": "blockedBy encodes dependency ordering."
},
{
"type": "tool_call",
"content": "worktree_run(name: \"auth-refactor\", command: \"pytest tests/auth -q\")\nworktree_run(name: \"ui-login\", command: \"npm test -- login\")",
"toolName": "worktree_run",
"annotation": "In this teaching runtime, commands route by lane-scoped cwd; other runtimes may use session-level directory switches. The invariant is explicit execution context."
"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",
"content": "worktree_keep(name: \"ui-login\")\nworktree_remove(name: \"auth-refactor\", complete_task: true)\nworktree_events(limit: 10)",
"toolName": "worktree_manager",
"annotation": "Closeout is explicit tool-driven state transition: mix keep/remove decisions and query lifecycle events in one pass"
"toolName": "complete_task",
"content": "{\"task_id\":\"task_tests\"}",
"annotation": "Completing a dependency can unblock downstream tasks."
},
{
"type": "system_event",
"content": "worktree.keep emitted for ui-login\nworktree.remove.before/after emitted for auth-refactor\ntask.completed emitted for #1\n.worktrees/events.jsonl appended",
"annotation": "Lifecycle transitions become explicit records while task/worktree files remain source-of-truth"
},
{
"type": "assistant_text",
"content": "Task board handles coordination, worktrees handle isolation. Parallel tracks stay clean and auditable.",
"annotation": "Coordinate in one board, isolate by lane only where needed, and run optional policy/audit side effects from lifecycle events"
"type": "tool_result",
"toolName": "complete_task",
"content": "Completed task_tests\nUnblocked: Deploy release",
"annotation": "The harness reports newly available work."
}
]
}

View File

@@ -0,0 +1,45 @@
{
"version": "s13",
"title": "Background Tasks",
"description": "Long-running work moves to a thread while the agent keeps the main loop responsive.",
"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."
},
{
"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": "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

@@ -0,0 +1,44 @@
{
"version": "s14",
"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": "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": "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": "schedule_cron",
"content": "Scheduled review-open-tasks with id cron_7.",
"annotation": "The job is written to the durable schedule store."
},
{
"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": "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

@@ -0,0 +1,51 @@
{
"version": "s15",
"title": "Agent Teams",
"description": "A lead agent can spawn teammate loops and coordinate through file-backed mailboxes.",
"steps": [
{
"type": "user_message",
"content": "Split this task: one agent checks tests, another reviews docs.",
"annotation": "The request needs parallel work with separate responsibilities."
},
{
"type": "assistant_text",
"content": "I'll spawn teammates with narrow roles and coordinate their updates through the message bus.",
"annotation": "The lead keeps orchestration authority while delegating execution."
},
{
"type": "tool_call",
"toolName": "spawn_teammate",
"content": "{\"name\":\"test-runner\",\"role\":\"Run test commands and report failures.\"}",
"annotation": "A teammate thread starts with its own simplified loop."
},
{
"type": "tool_call",
"toolName": "send_message",
"content": "{\"to\":\"test-runner\",\"message\":\"Run the focused test suite and summarize failures.\"}",
"annotation": "The lead sends work through the file-backed mailbox."
},
{
"type": "system_event",
"content": ".mailboxes/test-runner.jsonl <- {from:\"lead\", message:\"Run the focused test suite\"}",
"annotation": "MessageBus persists the handoff as JSONL."
},
{
"type": "system_event",
"content": ".mailboxes/lead.jsonl <- {from:\"test-runner\", message:\"Tests passed.\"}",
"annotation": "The teammate replies without sharing the lead's whole conversation."
},
{
"type": "tool_call",
"toolName": "check_inbox",
"content": "{\"agent\":\"lead\"}",
"annotation": "The lead polls its own inbox for teammate updates."
},
{
"type": "tool_result",
"toolName": "check_inbox",
"content": "test-runner: Tests passed.",
"annotation": "Mailbox messages re-enter the lead loop as tool results."
}
]
}

View File

@@ -0,0 +1,45 @@
{
"version": "s16",
"title": "Team Protocols",
"description": "Typed requests, request ids, and response matching turn team chat into a protocol.",
"steps": [
{
"type": "user_message",
"content": "Ask the reviewer agent for a plan before it edits anything.",
"annotation": "The lead needs a structured plan request, not free-form chat."
},
{
"type": "tool_call",
"toolName": "request_plan",
"content": "{\"to\":\"reviewer\",\"task\":\"Review the parser change before editing.\"}",
"annotation": "The protocol records a pending request with a request_id."
},
{
"type": "system_event",
"content": "pending_requests[req_18] = {kind:\"plan\", to:\"reviewer\", status:\"pending\"}",
"annotation": "ProtocolState keeps the request open until a matching response arrives."
},
{
"type": "system_event",
"content": "dispatch_message -> reviewer inbox: {type:\"plan_request\", request_id:\"req_18\"}",
"annotation": "The message is typed so the teammate knows how to handle it."
},
{
"type": "tool_call",
"toolName": "submit_plan",
"content": "{\"request_id\":\"req_18\",\"plan\":[\"inspect parser\",\"run fixtures\",\"report risks\"]}",
"annotation": "The teammate responds with the same request_id."
},
{
"type": "system_event",
"content": "match_response(req_18) -> status: ready_for_review",
"annotation": "The lead can correlate the reply with the exact request."
},
{
"type": "tool_call",
"toolName": "review_plan",
"content": "{\"request_id\":\"req_18\",\"approved\":true}",
"annotation": "The lead explicitly approves the plan before work proceeds."
}
]
}

View File

@@ -0,0 +1,46 @@
{
"version": "s17",
"title": "Autonomous Agents",
"description": "Idle teammates can scan the task board, claim eligible work, and return to idle after completion.",
"steps": [
{
"type": "system_event",
"content": "teammate(worker-a): state=IDLE -> idle_poll()",
"annotation": "Autonomy starts from an idle lifecycle tick, not a direct user command."
},
{
"type": "tool_call",
"toolName": "list_tasks",
"content": "{\"status\":\"open\"}",
"annotation": "The idle agent scans the shared task board."
},
{
"type": "tool_result",
"toolName": "list_tasks",
"content": "[{\"id\":\"task_5\",\"status\":\"open\",\"owner\":null,\"title\":\"Update README\"}]",
"annotation": "Only unclaimed work is eligible for autonomous pickup."
},
{
"type": "tool_call",
"toolName": "claim_task",
"content": "{\"id\":\"task_5\",\"owner\":\"worker-a\"}",
"annotation": "The task manager enforces ownership before work begins."
},
{
"type": "system_event",
"content": "worker-a: state=WORK task=task_5",
"annotation": "The lifecycle moves from IDLE to WORK."
},
{
"type": "tool_call",
"toolName": "complete_task",
"content": "{\"id\":\"task_5\",\"result\":\"README updated with setup notes.\"}",
"annotation": "Completion writes the result back to the shared board."
},
{
"type": "system_event",
"content": "worker-a: state=IDLE next_poll_in=5s",
"annotation": "After finishing, the agent becomes available for more work."
}
]
}

View File

@@ -0,0 +1,45 @@
{
"version": "s18",
"title": "Worktree Isolation",
"description": "A task can be bound to an isolated git worktree so concurrent agents avoid stepping on each other.",
"steps": [
{
"type": "user_message",
"content": "Let one teammate fix the docs while another changes the parser.",
"annotation": "Concurrent edits need isolated working directories."
},
{
"type": "tool_call",
"toolName": "create_worktree",
"content": "{\"task_id\":\"task_docs\",\"name\":\"docs-fix\"}",
"annotation": "The tool validates a safe worktree name before touching git."
},
{
"type": "system_event",
"content": "git worktree add .worktrees/docs-fix -b agent/docs-fix",
"annotation": "A separate branch and checkout are created for that task."
},
{
"type": "tool_result",
"toolName": "create_worktree",
"content": "task_docs bound to .worktrees/docs-fix",
"annotation": "The task record stores the assigned worktree path."
},
{
"type": "system_event",
"content": ".worktrees/events.jsonl <- {event:\"created\", task:\"task_docs\", worktree:\"docs-fix\"}",
"annotation": "Lifecycle events are emitted as a side channel."
},
{
"type": "tool_call",
"toolName": "keep_worktree",
"content": "{\"task_id\":\"task_docs\",\"reason\":\"needs human review\"}",
"annotation": "Closeout can preserve a worktree instead of deleting it immediately."
},
{
"type": "assistant_text",
"content": "The docs task now has an isolated worktree and can be reviewed independently from parser changes.",
"annotation": "The user sees the isolation boundary, not just a raw git command."
}
]
}

View File

@@ -0,0 +1,46 @@
{
"version": "s19",
"title": "MCP Tools",
"description": "The agent discovers external MCP tools and exposes them through a normalized tool namespace.",
"steps": [
{
"type": "user_message",
"content": "Connect the docs server and search for deployment guidance.",
"annotation": "The user asks for a tool source outside the built-in set."
},
{
"type": "tool_call",
"toolName": "connect_mcp",
"content": "{\"server\":\"docs\",\"command\":\"mock-docs-server\"}",
"annotation": "The runtime creates an MCP client for the named server."
},
{
"type": "system_event",
"content": "normalize_mcp_name(\"docs\", \"search\") -> mcp__docs__search",
"annotation": "External tools are namespaced to avoid collisions."
},
{
"type": "tool_result",
"toolName": "connect_mcp",
"content": "Connected docs with tools: mcp__docs__search, mcp__docs__read",
"annotation": "Tool discovery expands the active tool pool."
},
{
"type": "tool_call",
"toolName": "mcp__docs__search",
"content": "{\"query\":\"deployment\"}",
"annotation": "The LLM can now call the discovered tool by its normalized name."
},
{
"type": "tool_result",
"toolName": "mcp__docs__search",
"content": "[\"Deploy with npm run build\", \"Use environment variables for tokens\"]",
"annotation": "The external result is appended like any other tool result."
},
{
"type": "assistant_text",
"content": "The docs server says to build first and keep tokens in environment variables.",
"annotation": "The agent summarizes external tool output for the user."
}
]
}

View File

@@ -0,0 +1,57 @@
{
"version": "s20",
"title": "Comprehensive Agent",
"description": "The final agent composes context assembly, tools, memory, teams, background work, cron, worktrees, and MCP.",
"steps": [
{
"type": "user_message",
"content": "Plan a release: inspect tasks, run checks, delegate docs, and keep long scans in the background.",
"annotation": "The final lesson combines many earlier capabilities in one request."
},
{
"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'll inspect the task graph, delegate docs, start the slow scan in the background, and use the release checklist skill.",
"annotation": "The model chooses multiple mechanisms while keeping one coherent plan."
},
{
"type": "tool_call",
"toolName": "load_skill",
"content": "{\"name\":\"release-check\"}",
"annotation": "Skills contribute procedural context before execution."
},
{
"type": "tool_call",
"toolName": "spawn_teammate",
"content": "{\"name\":\"docs\",\"role\":\"Update release notes in its worktree.\"}",
"annotation": "Team delegation handles a parallel slice of the release."
},
{
"type": "tool_call",
"toolName": "bash",
"content": "npm run lint && npm run test",
"toolInput": "{\"background\":true}",
"annotation": "Long-running validation goes through the background task path."
},
{
"type": "tool_call",
"toolName": "connect_mcp",
"content": "{\"server\":\"deploy\",\"command\":\"mock-deploy-server\"}",
"annotation": "External capabilities are added only when needed."
},
{
"type": "system_event",
"content": "recover: background task done, teammate replied, MCP deploy status ready",
"annotation": "The integrated runtime folds asynchronous results back into the loop."
},
{
"type": "assistant_text",
"content": "Release preparation is ready: checks passed, docs are updated in the teammate worktree, and deployment status is available.",
"annotation": "The comprehensive agent returns a single user-facing outcome."
}
]
}