Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Learn Claude Code — Admin & Architecture Guide

Generated June 2026 from shareAI-lab/learn-claude-code at ec9ea87

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the deep reference for developers, architects, and contributors working on Learn Claude Code — and for anyone deploying its web platform. It documents the harness architecture each lesson builds, the on-disk state each mechanism uses, the web application's structure and build pipeline, the security model (including what is deliberately left unguarded), code patterns, testing, and known limitations.

For installation and how to take the course, see the companion User Guide. This document assumes you want to understand or extend the implementation.


1. Executive Summary

What it is. A 0-to-1 curriculum in harness engineering — the discipline of building the operational environment around an agent model. The project's thesis: agency comes from the model (trained weights), while the harness (tools, knowledge, observation, action interfaces, permissions) is what engineers build. An agent product = model + harness.

What it contains. Two coordinated deliverables:

  1. A Python course — twenty standalone, runnable agent programs (s01_agent_loop/code.py … s20_comprehensive/code.py). Each is the previous program plus exactly one new harness mechanism, layered around an agent loop that never changes.
  2. A web platform (web/) — a Next.js 16 static site that renders the course with interactive per-mechanism visualizations, code diffs, and architecture diagrams, in English / Chinese / Japanese.

Two tracks. The canonical current track is the root-level s01–s20 folders (20 mechanisms). A legacy 12-lesson track survives in agents/ and docs/, and the live web app currently renders that legacy track. Chapter numbers differ between tracks.

Tech footprint. Python ≥ 3.10 with three dependencies (anthropic, python-dotenv, pyyaml). The web app is Next.js 16 / React 19 / Tailwind 4, statically exported. No database, no server runtime for the lessons — the filesystem is the only persistence layer (.memory/, .tasks/, .mailboxes/, .worktrees/, .transcripts/, .scheduled_tasks.json).

Design center. Every mechanism is additive and legible: carried-over code is labelled # FROM sNN (unchanged), and each file documents in its module docstring exactly what it adds. Production concerns (rule-based permission governance, full hook bus, real MCP transport/OAuth, session resume/fork) are intentionally simplified — the project is explicit that it teaches "key designs," not a production-grade clone.


2. System Architecture

2.1 The two halves

┌──────────────────────────────────────────────────────────────────────┐
│  TEACHING CODE (Python)                                                │
│                                                                        │
│   s01_agent_loop/code.py ── s02 ── s03 ── … ── s20_comprehensive       │
│        │ each = prev + one mechanism                                   │
│        │ talks to: Anthropic-compatible Messages API                   │
│        │ persists to: ./.memory ./.tasks ./.mailboxes ./.worktrees …   │
│                                                                        │
│   agents/  (legacy 12-lesson runnable copies + s_full.py)              │
│   skills/  (agent-builder, code-review, mcp-builder, pdf — used by s07)│
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│  WEB PLATFORM (web/, Next.js 16, static export)                        │
│                                                                        │
│   docs/{en,zh,ja}/*.md ──► scripts/extract-content.ts (predev/prebuild)│
│        │                         │ generates web/src/data/generated/   │
│        ▼                         ▼                                      │
│   App Router  ──►  visualizations/  +  simulator/  +  diff/  +  i18n/   │
│   (static HTML out/)                                                   │
└──────────────────────────────────────────────────────────────────────┘

The two halves are loosely coupled: the web app consumes the docs/ markdown (legacy track) at build time via an extraction script; the Python lessons have no dependency on the web app.

2.2 The canonical agent loop

Every lesson is built around one invariant pattern (this is the literal core; later lessons wrap it but never alter its shape):

def agent_loop(messages):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM,
            messages=messages, tools=TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":     # model is done
            return

        results = []
        for block in response.content:
            if block.type == "tool_use":
                output = TOOL_HANDLERS[block.name](**block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output,
                })
        messages.append({"role": "user", "content": results})   # feed back, loop

The model decides when to call tools and when to stop (stop_reason); the harness only executes what's asked and feeds results back. The tool_use / tool_result contract (matching tool_use_id) is the load-bearing protocol — several known issues (see §11) stem from accidentally breaking that pairing during compaction.

2.3 The layered mechanism model

The web app's web/src/lib/constants.ts defines the authoritative grouping of the twenty mechanisms into five architectural layers. This is the project's own conceptual map:

Layer Color Mechanisms
Tools & Execution #3B82F6 s01, s02, s03, s04
Planning & Control #10B981 s05, s06, s07, s10, s11
Memory Management #8B5CF6 s08, s09
Concurrency & Scheduling #F59E0B s13, s14
Multi-Agent Platform #EF4444 s12, s15, s16, s17, s18, s19, s20

The same file also stores per-version metadata (title, subtitle, coreAddition, keyInsight, layer, prevVersion) for all twenty chapters and the canonical VERSION_ORDER (s01…s20) — this is the single source the web UI uses to render the learning path and navigation.


3. Repository Structure

learn-claude-code/
├── README.md / README-zh.md / README-ja.md   # project thesis + chapter index
├── LICENSE                                    # MIT
├── requirements.txt                           # anthropic, python-dotenv, pyyaml
├── .env.example                               # required+optional env, provider table
│
├── s01_agent_loop/ … s20_comprehensive/       # CURRENT 20-lesson track
│     ├── README.md         # Chinese narrative (source)
│     ├── README.en.md      # English translation
│     ├── README.ja.md      # Japanese translation
│     ├── code.py           # standalone runnable agent
│     └── images/           # SVG diagrams (some chapters)
│   (s05_todo_write/example/hello.py — a sample target file)
│
├── agents/                                    # LEGACY 12-lesson runnable copies
│     ├── s01_agent_loop.py … s12_worktree_task_isolation.py
│     ├── s_full.py          # legacy "everything" agent
│     └── __init__.py
│
├── skills/                                    # SKILL.md packs loaded by s07
│     ├── agent-builder/  (+ references/, scripts/)
│     ├── code-review/
│     ├── mcp-builder/
│     └── pdf/
│
├── docs/{en,zh,ja}/                           # LEGACY 12-lesson docs (web source)
│     └── s01-the-agent-loop.md … s12-worktree-task-isolation.md
│
├── tests/                                     # pytest smoke tests
│     ├── test_agents_smoke.py
│     ├── test_s_full_background.py
│     └── test_todo_write_string_input.py
│
└── web/                                        # Next.js 16 learning platform
      ├── next.config.ts        # output:"export", trailingSlash, images.unoptimized
      ├── package.json          # predev/prebuild → npm run extract
      ├── scripts/extract-content.ts
      └── src/
            ├── app/[locale]/(learn)/…          # App Router, localized routes
            ├── components/visualizations/      # per-mechanism animated viz (entry: index.tsx)
            ├── components/{simulator,diff,timeline,architecture,code,docs,ui,layout}/
            ├── data/{annotations,scenarios,generated}/
            ├── i18n/messages/{en,zh,ja}.json
            └── lib/{constants.ts, i18n.tsx, i18n-server.ts, utils.ts}

Note

The legacy-to-current chapter mapping is documented in the root README.md. Notable shifts: legacy s03→current s05 (TodoWrite), legacy s06→current s08 (Context Compact), legacy s07→current s12 (Task System), legacy s12→current s18 (Worktree). Mechanisms s03, s04, s09, s10, s11, s14, s19, s20 are new in the current track.


4. The Harness — Architecture Deep Dive

This section documents each mechanism's internals: the data structures it introduces, the on-disk state it owns, and the integration point in the loop. Mechanisms compose additively, so later chapters inherit everything described earlier.

4.1 Tools & Execution (s01–s04)

s01 — Agent Loop. One tool (bash), one while loop. run_bash runs subprocess.run(shell=True, timeout=120), returns combined stdout+stderr truncated to 50 000 chars, with a tiny inline deny-list. MODEL is read from os.environ["MODEL_ID"] (hard requirement); the client is Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL")).

s02 — Tool Use. Adds read_file, write_file, edit_file, glob, and the central TOOL_HANDLERS dispatch map. Introduces safe_path() — resolves a path against WORKDIR and raises ValueError("Path escapes workspace") if it escapes. The loop changes one line: output = TOOL_HANDLERS[block.name](**block.input).

s03 — Permission. A three-gate pipeline before execution:

  • Gate 1 — deny-list: DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if=", "> /dev/sda"] (bash only).
  • Gate 2 — rule matching: PERMISSION_RULES flag writes outside the workspace and destructive bash keywords (rm , > /etc/, chmod 777).
  • Gate 3 — user approval: interactive Allow? [y/N] prompt. Integration is one line: if not check_permission(block): continue.

s04 — Hooks. Extracts cross-cutting logic onto a HOOKS registry keyed by event: UserPromptSubmit, PreToolUse, PostToolUse, Stop. register_hook(event, cb) / trigger_hooks(event, *args); a hook returning non-None short-circuits (used to block a tool call). The s03 permission check is reimplemented as a PreToolUse hook. The loop no longer contains the permission logic — it calls trigger_hooks("PreToolUse", block).

4.2 Planning & Control (s05, s06, s07, s10, s11)

s05 — TodoWrite. Adds the todo_write tool over in-memory CURRENT_TODOS (each item {content, status} where status ∈ pending|in_progress|completed). _normalize_todos accepts a list or a JSON/ast.literal_eval string (robustness against the model passing a stringified array — see the dedicated test). A "nag" counter injects <reminder>Update your todos.</reminder> after 3 tool rounds without a todo_write.

s06 — Subagent. Adds the task tool → spawn_subagent(description). The sub-agent runs its own loop with a fresh messages=[{"role":"user","content":description}], a distinct SUB_SYSTEM, and a restricted SUB_TOOLS set that excludes task (no recursion). Capped at 30 turns. Returns only the final text (with a fallback search backward for the last assistant text, else "Subagent stopped after 30 turns…"). Intermediate history is discarded — this is the context-isolation lesson.

s07 — Skill Loading. Two-level knowledge injection. At startup _scan_skills() walks skills/*/SKILL.md, parses YAML frontmatter (name, description), and builds SKILL_REGISTRY. The system prompt carries only names+descriptions (cheap, ~100 tokens each via build_system()); the full body is fetched on demand by the load_skill(name) tool (registry lookup — no path traversal). Bundled skills: agent-builder, code-review, mcp-builder, pdf.

s10 — System Prompt. Runtime prompt assembly. PROMPT_SECTIONS (identity, tools, workspace, memory) are selected and joined by assemble_system_prompt(context) based on real state (e.g. memory section loads only if .memory/MEMORY.md exists). get_system_prompt(context) caches via json.dumps(context, sort_keys=True) — the docstring explicitly notes Python's hash() is unsuitable (process randomization, fails on nested structures). update_context() re-derives context after each tool round.

s11 — Error Recovery. Wraps the model call in classified recovery:

  • Path 1 — max_tokens: escalate 8000 → 64000 (retry without appending truncated output), then up to MAX_RECOVERY_RETRIES=3 continuation prompts.
  • Path 2 — prompt too long: reactive_compact once (trims to last 5 messages with a marker).
  • Path 3 — 429/529: with_retry does exponential backoff with jitter (BASE_DELAY_MS=500, cap 32 s, MAX_RETRIES=10); after MAX_CONSECUTIVE_529=3 it switches to FALLBACK_MODEL_ID if set. State lives in a RecoveryState object. Raises RuntimeError("Max retries … exceeded") at the ceiling.

4.3 Memory Management (s08, s09)

s08 — Context Compact. A four-layer pipeline run before each model call, "cheap first, expensive last" (order matches Claude Code's source: budget → snip → micro → auto):

  • L1 snip_compact — when len(messages) > 50, keep 3 head + tail, replace the middle with a [snipped N messages] marker.
  • L2 micro_compact — replace all but the last KEEP_RECENT=3 tool results (>120 chars) with a placeholder.
  • L3 tool_result_budget — when the latest turn's tool results exceed 200 KB, spill the largest (> PERSIST_THRESHOLD=30000) to .task_outputs/tool-results/<id>.txt and leave a <persisted-output> preview.
  • L4 compact_history — if estimate_size(messages) > CONTEXT_LIMIT=50000, write a transcript to .transcripts/ and replace history with a one-call LLM summary.
  • Emergency reactive_compact — on an API "prompt too long", summarize + keep the last 5 messages, retry once. Adds a compact tool that triggers L4 on demand.

s09 — Memory. Persistent cross-session knowledge in .memory/. Three subsystems:

  • Selection — select_relevant_memories() asks the model (with keyword fallback) which memory files match the recent conversation; load_memories() injects the chosen files' content into the current turn.
  • Extraction — after each turn, extract_memories() runs over a pre-compression snapshot and writes new {name, type, description, body} files (types: user|feedback|project|reference) with YAML frontmatter; _rebuild_index() keeps MEMORY.md (one line per memory) in sync.
  • Consolidation — when files ≥ CONSOLIDATE_THRESHOLD=10, consolidate_memories() merges duplicates / drops stale entries via an LLM call, targeting ≤30 memories. The MEMORY.md index is injected into the system prompt every turn (cheap); full bodies load on demand.

4.4 Concurrency & Scheduling (s13, s14)

s13 — Background Tasks. threading.Thread daemon execution. should_run_background() honors an explicit run_in_background tool param, else falls back to is_slow_operation() (keyword heuristic: install, build, test, deploy, pip install, npm install, pytest, make, …). start_background_task() dispatches to a daemon worker, returns a bg_NNNN id immediately, and stores results under a threading.Lock. collect_background_results() drains completed jobs and injects <task_notification> blocks into the next user message — deliberately not reusing the original tool_use_id.

s14 — Cron Scheduler. Four cooperating layers:

  1. Scheduler thread (cron_scheduler_loop) polls every 1 s, matches jobs, and appends fired jobs to cron_queue. Uses a date-aware "%Y-%m-%d %H:%M" marker (_last_fired) so a job fires at most once per calendar minute. Per-job errors are caught so one bad job can't kill the thread.
  2. Queue — cron_queue decouples the scheduler from the agent.
  3. Queue processor (queue_processor_loop) wakes the agent (under agent_lock, non-blocking acquire) when work is queued and the agent is idle.
  4. Consumer — agent_loop drains consume_cron_queue() and injects [Scheduled] <prompt> messages. CronJob(id, cron, prompt, recurring, durable). cron_matches implements 5-field matching with *, */step, ranges, and lists, and standard DOM/DOW OR semantics. validate_cron bounds-checks each field. Durable jobs persist to .scheduled_tasks.json and reload (with re-validation) on startup.

4.5 Multi-Agent Platform (s12, s15–s20)

s12 — Task System. The durable foundation for multi-agent work. Task(id, subject, description, status, owner, blockedBy) persisted one-JSON-file-per-task in .tasks/. can_start() treats missing dependencies as blocked; claim_task() sets owner + in_progress (rejecting already-claimed); complete_task() reports newly unblocked downstream tasks. Five tools: create_task, list_tasks, get_task, claim_task, complete_task.

s15 — Agent Teams. A file-based MessageBus over .mailboxes/*.jsonl. spawn_teammate_thread runs a teammate in a background thread with its own simplified loop (bash, read, write, send_message); teammate→Lead messages are injected into the Lead's history (not merely printed). Permissions "bubble" — teammates run the same PreToolUse hooks. (Teaching version caps teammate rounds; production uses an idle loop, introduced next.)

s16 — Team Protocols. Request/response contracts with correlation. ProtocolState(request_id, type, sender, status, created_at) + a pending_requests registry. dispatch_message routes by type to handlers (handle_shutdown_request, handle_plan_response, …); match_response correlates replies by request_id with type validation. Adds shutdown handshake and plan request/approval; teammates switch to an idle loop (wait for inbox) instead of exiting after N rounds.

s17 — Autonomous Agents. Self-organization. scan_unclaimed_tasks finds pending, unowned tasks whose deps are complete; teammates auto-claim and run a WORK → IDLE → SHUTDOWN lifecycle (idle poll ~5 s; shutdown dispatch when idle ~60 s). Identity is re-injected after context compression so a teammate doesn't forget who it is.

s18 — Worktree Isolation. Filesystem isolation to match the context isolation. Task gains a worktree: str | None field. validate_worktree_name rejects path traversal and illegal chars (1–40 chars: letters, digits, ., _, -). create_worktree does git worktree add under .worktrees/<name> on branch wt/<name>; tasks bind by name; an event log is written to .worktrees/events.jsonl. run_git returns (ok, output) and logs events only on success. Requires a git repo (RuntimeError otherwise). Tools: create_worktree, remove_worktree, keep_worktree.

s19 — MCP Plugin. External-tool bridge. MCPClient discovers tools from a server and assemble_tool_pool merges builtin + MCP tools into one dynamic pool. Tool names are normalized to mcp__{server}__{tool}; MCP tools carry readOnly/destructive annotations. Because the pool is dynamic, prompt caching is disabled for the assembled tools. (The transport here is a mock handler; real MCP transport, OAuth, resource subscription, and polling are out of scope — see §11.)

s20 — Comprehensive Agent. The integration finale: every mechanism above in one harness around the same loop. It re-tunes some constants for the combined setting (e.g. ESCALATED_MAX_TOKENS=16000, MAX_RETRIES=3, MAX_CONSECUTIVE_529=2) and adds a thread-safe terminal_print that redraws the prompt line when background threads emit output during interactive use.


5. The Web Platform

5.1 Stack & build model

Concern Choice
Framework Next.js 16.1.6 (App Router)
UI React 19.2.3, Tailwind CSS 4, framer-motion 12 (animation), lucide-react (icons)
Markdown unified + remark-parse / remark-gfm / remark-rehype + rehype-raw / rehype-highlight / rehype-stringify
Diffing diff 8
Output Static export (output: "export", trailingSlash: true, images.unoptimized: true) → plain HTML in out/
Scripting tsx runs scripts/extract-content.ts

next.config.ts:

const nextConfig: NextConfig = {
  output: "export",
  images: { unoptimized: true },
  trailingSlash: true,
};

Because the site is a static export, it has no server runtime — it can be hosted on any static host or CDN.

5.2 Content pipeline

package.json wires a pre-step into both dev and build:

"scripts": {
  "extract": "tsx scripts/extract-content.ts",
  "predev":  "npm run extract",
  "dev":     "next dev",
  "prebuild":"npm run extract",
  "build":   "next build",
  "start":   "next start"
}

npm run dev and npm run build therefore always regenerate extracted content first (into web/src/data/generated/) from the docs/ markdown. Operators never run extraction manually.

Important

The web app currently sources the legacy docs/ (s01–s12) track, while the canonical learning material is the root-level s01–s20 folders. Keep this in mind when reconciling what the site shows against the current chapters.

5.3 Internationalization

A lightweight, dependency-free i18n: JSON message catalogs at web/src/i18n/messages/{en,zh,ja}.json, a client I18nProvider/useTranslations context (lib/i18n.tsx), and a server variant (lib/i18n-server.ts). Both fall back to English on a missing key, then to the raw key. Routes are localized under app/[locale]/(learn)/….

5.4 Visualization architecture

web/src/components/visualizations/index.tsx is the entry point: a Record<version, React.lazy(...)> map (s01–s20) wrapped in Suspense, so each mechanism's animated diagram is code-split and loaded on demand. lib/constants.ts drives the navigation (VERSION_ORDER, VERSION_META, LAYERS). Supporting component groups: simulator/ (interactive agent simulation), diff/ (chapter-to-chapter code diffs), timeline/, architecture/, code/, docs/, plus ui/ and layout/ primitives. Data lives in web/src/data/{annotations,scenarios,generated}/.


6. Requirements & Configuration Reference

6.1 Environment variables

Variable Required Read by Purpose
ANTHROPIC_API_KEY Yes Anthropic SDK (implicit) API credential (or provider key when ANTHROPIC_BASE_URL is set)
MODEL_ID Yes os.environ["MODEL_ID"] (every lesson) Model to call; default claude-sonnet-4-6
ANTHROPIC_BASE_URL No os.getenv (s07+ and all later) Point at an Anthropic-compatible provider
FALLBACK_MODEL_ID No s11, s20 Model to switch to after repeated 529 overloads

Note

When ANTHROPIC_BASE_URL is set, the lessons proactively os.environ.pop("ANTHROPIC_AUTH_TOKEN", None) so the provider key in ANTHROPIC_API_KEY is authoritative.

6.2 Provider matrix (from .env.example)

Provider MODEL_ID International ANTHROPIC_BASE_URL China-mainland base URL
Anthropic claude-sonnet-4-6 (default) (default)
MiniMax MiniMax-M2.5 https://api.minimax.io/anthropic https://api.minimaxi.com/anthropic
GLM (Zhipu) glm-5 https://api.z.ai/api/anthropic https://open.bigmodel.cn/api/anthropic
Kimi (Moonshot) kimi-k2.5 https://api.moonshot.ai/anthropic https://api.moonshot.cn/anthropic
DeepSeek deepseek-chat https://api.deepseek.com/anthropic (same global endpoint)

6.3 On-disk state (created in the working directory)

Path Owner mechanism Contents
.memory/ + MEMORY.md s09 Memory files (md + YAML frontmatter) and the index
.tasks/task_*.json s12+ One JSON file per task
.scheduled_tasks.json s14 Durable cron jobs
.mailboxes/*.jsonl s15+ Teammate message bus
.worktrees/<name> + .worktrees/events.jsonl s18 Git worktrees + event log
.transcripts/transcript_*.jsonl s08+ Pre-compaction history snapshots
.task_outputs/tool-results/<id>.txt s08+ Spilled large tool outputs

Tip

To reset any lesson to a clean state, delete the relevant dot-directories from your working directory. They are regenerated on demand.


7. Deployment (Web Platform)

The lessons are not "deployed" — they run locally. Only the web platform is deployable, and because it is a static export, deployment is straightforward.

cd web
npm install
npm run build          # runs extract → next build → static HTML in out/

Deployment checklist:

  • [ ] Node.js (current LTS) and npm available on the build host.
  • [ ] npm run build completes and produces web/out/.
  • [ ] Serve web/out/ from any static host or CDN (e.g. a bucket + CDN, Nginx, GitHub Pages, Vercel static).
  • [ ] Because trailingSlash: true, ensure your host serves path/ → path/index.html.
  • [ ] Because images.unoptimized: true, no image-optimization server is needed.
  • [ ] Verify all three locales (/en, /zh, /ja) render.

Caution

There is no backend, no auth, and no secrets in the web build — it is purely static content. Do not add the Python .env (API keys) anywhere into web/; the website never calls the model API.


8. Security Analysis

This is a teaching harness. Several mechanisms are exactly the security controls being taught — and the project is explicit that some production protections are intentionally omitted. Treat the agent as a program that runs arbitrary shell commands on your machine.

8.1 Controls that exist

Control Where What it stops
Hard deny-list s03/s04+ (DENY_LIST) rm -rf /, sudo, shutdown, reboot, mkfs, dd if=, raw device writes
Destructive-command prompt s03/s04 rm , > /etc/, chmod 777 → interactive approval
Workspace sandboxing s02+ (safe_path) File reads/writes resolving outside WORKDIR → ValueError: Path escapes workspace
Worktree name validation s18 (validate_worktree_name) Path traversal / illegal characters in worktree names
Skill lookup by registry s07 (load_skill) Path traversal via skill names (registry lookup, not filesystem path join)
Sub-agent recursion cap s06 Runaway recursion (no task tool in SUB_TOOLS; 30-turn limit)
MCP tool annotations s19 Surfaces readOnly/destructive so a harness can gate accordingly

8.2 Intentional gaps (do not rely on this as a sandbox)

The project's own "Scope" section lists these as deliberately simplified or omitted:

  • Full event/hook bus (PreToolUse, SessionStart/End, ConfigChange) — only minimal lifecycle events are wired.
  • Rule-based permission governance and trust workflows — the deny-list + interactive approval is a teaching subset, not a policy engine.
  • Session lifecycle controls (resume/fork) and complete worktree lifecycle handling.
  • Real MCP runtime — transport, OAuth, resource subscription, and polling are mocked.
  • The JSONL mailbox protocol is a teaching implementation, not a claim about any production internal.

Warning

subprocess.run(shell=True, …) executes whatever the model emits. The deny-list is substring-based and easily bypassed by a determined or confused model (e.g. obfuscated commands). Run lessons in a disposable directory / VM, never against production systems or sensitive data. This is acceptable for a learning tool and is itself part of what the permission chapters teach you to harden.

8.3 Secret handling

Credentials live only in the Python-side .env (gitignored). The web build contains no secrets and makes no API calls. There is no telemetry; lessons talk only to the configured model endpoint.


9. Code Quality & Patterns

  • Additive, legible layering. Each code.py is the previous file plus one section, with carried-over code explicitly marked # FROM sNN (unchanged) and a module docstring enumerating the diff. This makes the codebase a teaching artifact where a chapter-to-chapter diff is the lesson.
  • Dispatch-map extensibility. Capabilities register into TOOL_HANDLERS; the loop body is invariant. Adding a tool = add an entry, not edit the loop.
  • Filesystem as state machine. No DB, no broker. Tasks, memory, mailboxes, worktrees, schedules, and transcripts are all plain files/dirs — durable, inspectable, and resettable by deletion. (This mirrors a pattern production agents also use.)
  • Dataclasses for records. Task, CronJob, ProtocolState, RecoveryState are dataclasses serialized with asdict + json.
  • Defensive parsing. _normalize_todos accepts both real lists and stringified JSON/Python; _parse_frontmatter tolerates missing/invalid YAML; cron fields are bounds-validated before use.
  • Threading model. Background tasks (s13), the cron scheduler/queue processor (s14), and teammates (s15+) all use daemon threads with threading.Lock around shared dicts and an agent_lock to serialize agent turns.
  • Deterministic prompt caching. s10 caches the assembled system prompt via json.dumps(sort_keys=True) rather than hash() (documented rationale: process-randomized hashing and unhashable nested structures).

Conventions: Python ≥ 3.10 syntax (str | None, Path.is_relative_to); ANSI color in terminal output; module docstrings carry ASCII architecture diagrams; bilingual prompts (some chapters print Chinese REPL hints).


10. Testing Strategy

tests/ contains focused smoke tests (pytest), not exhaustive coverage:

Test Verifies
test_agents_smoke.py The legacy agents/ modules import/run end-to-end without crashing
test_s_full_background.py The legacy "everything" agent's background-task path
test_todo_write_string_input.py todo_write accepts a stringified-JSON todos arg (the _normalize_todos robustness path)

Note

A community "Production Readiness Scan" (issue #169) scored the repo low on testing/error-handling — by design. These are teaching programs optimized for clarity, not a production library; the test suite is a sanity net, not a guarantee. Contributors adding mechanisms should add at least a smoke test that the lesson imports and completes a trivial turn.


11. Known Limitations & Improvement Areas

Drawn from the project's "Scope" section and its open community issues:

  • Compaction can split tool_use/tool_result pairs (issues #325, and the 400 BadRequest reports #345/#55). Breaking that pairing makes the next request invalid. A robust fix keeps each tool_use block adjacent to its matching tool_result when trimming/snipping.
  • 400 BadRequest in s13 background tasks (#345) — likely the same pairing problem under concurrent notification injection.
  • s09 memory build_system() placement (#343) — noted as being rebuilt inside a loop where it could be hoisted; an efficiency cleanup.
  • s10 build_system_reminder usage (#279) — example code references a reminder builder whose call site is incomplete.
  • Teammate edge cases (#291) — a Lead can exit early if a teammate runs a long tool-less task and the Lead sees no tool calls; the idle/shutdown heuristics need tightening for long teammate work.
  • Context-compression vs. task-system design discussion (#174) — open design debate about the s08/s12 trade-offs.
  • Intentionally simplified for teaching: full hook bus, rule-based permission governance, session resume/fork, complete worktree lifecycle, and real MCP transport/OAuth/subscription. These are the natural "productionization" exercises after the course.
  • Permission deny-list is substring-based — adequate for teaching, trivially bypassable in adversarial use; a real harness needs structured command parsing and policy rules.

Tip

The README also points to the maintainers' productization follow-ons (a CLI coding agent and an embeddable agent SDK) and a sister tutorial on "always-on" harnesses (heartbeat + cron + IM channels + memory + personality) — the recommended next steps once the twenty mechanisms are understood.


12. Quick Reference

Run a lesson: python sNN_<name>/code.py · quit with q/exit/empty line. Required env: ANTHROPIC_API_KEY, MODEL_ID (default claude-sonnet-4-6). Optional: ANTHROPIC_BASE_URL, FALLBACK_MODEL_ID. Deps: pip install -r requirements.txt (anthropic, python-dotenv, pyyaml). Web: cd web && npm install && npm run dev (→ :3000) · build: npm run build (→ web/out/).

Mechanism → tools added:

Chapter Adds New tools
s01 Agent loop bash
s02 Tool dispatch read_file, write_file, edit_file, glob
s03 Permission gates —
s04 Hook registry —
s05 Planning todo_write
s06 Sub-agents task
s07 Skills load_skill
s08 Compaction compact
s09 Memory — (.memory/)
s10 Prompt assembly —
s11 Error recovery —
s12 Task board create_task, list_tasks, get_task, claim_task, complete_task
s13 Background exec run_in_background param on bash
s14 Cron schedule_cron, list_crons, cancel_cron
s15 Teams spawn_teammate, send_message, check_inbox
s16 Protocols request_shutdown, request_plan, review_plan, submit_plan
s17 Autonomy (teammate gains list_tasks, claim_task, complete_task)
s18 Worktrees create_worktree, remove_worktree, keep_worktree
s19 MCP mcp__{server}__{tool} (dynamic)
s20 Everything all of the above

On-disk state: .memory/ · .tasks/ · .scheduled_tasks.json · .mailboxes/ · .worktrees/ · .transcripts/ · .task_outputs/.

License: MIT.