Skip to content

Claude / OpenAI SDK patterns

SDK Patterns: Where Identity, Config, and Knowledge Live

Section titled “SDK Patterns: Where Identity, Config, and Knowledge Live”

Research date: 2026-05-11 Scope: Production-grade agent SDKs — how they recommend laying out persona, runtime config, shared knowledge, and folder structure for fleets of named agents.

Frameworks studied:

  1. Anthropic Claude Agent SDK (Python + TypeScript)
  2. Claude Code (CLAUDE.md hierarchy, subagents, plugins)
  3. Agent Skills open standard (SKILL.md spec)
  4. OpenAI Assistants API + successor Responses API / Prompts
  5. OpenAI Agents SDK (Python, code-defined)

For each: concrete answers to the six design questions, then a comparison table, ranked patterns worth stealing, and antipatterns flagged in the docs.


1. Claude Code — CLAUDE.md hierarchy, subagents, skills

Section titled “1. Claude Code — CLAUDE.md hierarchy, subagents, skills”

This is the closest analog to a fleet of named persistent agents sharing a common ethos. It’s the only one that explicitly ships file-on-disk identity plus an import/cascade system.

A persistent agent’s identity is a Markdown file with YAML frontmatter, not a programmatic object.

For subagents (the canonical “named persona” pattern in Claude Code):

.claude/agents/<agent-name>.md # project scope
~/.claude/agents/<agent-name>.md # user scope (all projects)

Example file (https://code.claude.com/docs/en/sub-agents#write-subagent-files):

---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---
You are a code reviewer. When invoked, analyze the code and provide
specific, actionable feedback on quality, security, and best practices.

The Markdown body is the system prompt. The frontmatter is the runtime config. They live in one file because the docs treat the body and the config as a single unit of agent identity:

“The body becomes the system prompt that guides the subagent’s behavior. Subagents receive only this system prompt (plus basic environment details like working directory), not the full Claude Code system prompt.” — code.claude.com/docs/en/sub-agents

When the agent is the main session (not a subagent), identity lives in CLAUDE.md, which is additive context rather than a replacement system prompt:

“CLAUDE.md content is delivered as a user message after the system prompt, not as part of the system prompt itself.” — code.claude.com/docs/en/memory

Hierarchy precedence (https://code.claude.com/docs/en/memory#choose-where-to-put-claude-md-files):

ScopePathLoaded
Managed policy/Library/Application Support/ClaudeCode/CLAUDE.md (macOS), /etc/claude-code/CLAUDE.md, C:\Program Files\ClaudeCode\CLAUDE.mdAll sessions on machine
Project./CLAUDE.md or ./.claude/CLAUDE.mdProject sessions
User~/.claude/CLAUDE.mdAll your sessions
Local./CLAUDE.local.md (gitignored)Just you, this project

Subagent identity precedence (sub-agents doc, “Choose the subagent scope”):

1 (highest) Managed settings
2 --agents CLI flag (session only, JSON)
3 .claude/agents/ (project)
4 ~/.claude/agents/ (user)
5 (lowest) Plugin's agents/ directory

Runtime config (model, tools, hooks, MCP servers, permissions, memory scope) lives in the same file as the persona, in the YAML frontmatter.

Full subagent frontmatter spec (https://code.claude.com/docs/en/sub-agents#supported-frontmatter-fields):

---
name: code-reviewer # required, lowercase + hyphens
description: ... # required, when to delegate
tools: Read, Grep, Glob # allowlist
disallowedTools: Write, Edit # denylist
model: sonnet | opus | haiku | claude-opus-4-7 | inherit
permissionMode: default|acceptEdits|auto|dontAsk|bypassPermissions|plan
maxTurns: 10
skills: [api-conventions, error-handling-patterns] # preloaded
mcpServers:
- playwright:
type: stdio
command: npx
args: ["-y", "@playwright/mcp@latest"]
- github # reference to already-configured server
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/validate.sh"
memory: user | project | local # enables ~/.claude/agent-memory/<name>/
background: false
effort: low|medium|high|xhigh|max
isolation: worktree # spawn in temp git worktree
color: blue
initialPrompt: "Auto-submitted as first turn"
---

Settings that are environment-wide (not per-agent) live separately in settings.json files. Precedence is the standard Claude Code layered model: managed > user > project > local. From https://code.claude.com/docs/en/memory#manage-claude-md-for-large-teams, settings handle “technical enforcement” while CLAUDE.md handles “behavioral guidance”:

ConcernLives in
Block tools / commands / file pathssettings.json permissions.deny
Sandbox isolationsettings.json sandbox.enabled
Env vars and API routingsettings.json env
Behavioral instructions for ClaudeCLAUDE.md
Code style and quality guidelinesCLAUDE.md

For named subagents: same file. Frontmatter (config) + body (persona) are atomic. The docs don’t split them, and the /agents interactive UI treats them as one editable unit.

For the main session / persistent agent identity: split. CLAUDE.md carries persona-ish content (rules, conventions, project memory), while settings.json carries runtime enforcement (permissions, sandbox, hooks, env). The docs draw a hard line:

“Settings rules are enforced by the client regardless of what Claude decides to do. CLAUDE.md instructions shape Claude’s behavior but are not a hard enforcement layer.” — code.claude.com/docs/en/memory

Reasoning given: “behavioral guidance” is interpretive (loaded as a user message), while “technical enforcement” must be deterministic (loaded by the harness, not the model). Splitting them prevents an agent from talking itself out of a hard rule.

Claude Code has explicit support for shared-doctrine-plus-specialization via two mechanisms:

Mechanism A — @path imports inside CLAUDE.md (https://code.claude.com/docs/en/memory#import-additional-files):

“CLAUDE.md files can import additional files using @path/to/import syntax. Imported files are expanded and loaded into context at launch alongside the CLAUDE.md that references them. Both relative and absolute paths are allowed. Relative paths resolve relative to the file containing the import, not the working directory. Imported files can recursively import other files, with a maximum depth of five hops.”

Example pattern:

# Per-agent CLAUDE.md
@~/.claude/docs/fleet-ethos.md
@~/.claude/docs/fleet-source-order.md
@./persona.md
## Agent-specific overrides
...

This is exactly how Wes’s fleet already composes ethos + persona, and the docs sanction it.

Mechanism B — directory cascade (https://code.claude.com/docs/en/memory#how-claude-md-files-load):

“Claude Code reads CLAUDE.md files by walking up the directory tree from your current working directory, checking each directory along the way for CLAUDE.md and CLAUDE.local.md files. … All discovered files are concatenated into context rather than overriding each other. Across the directory tree, content is ordered from the filesystem root down to your working directory.”

Order: root-most → cwd-most → CLAUDE.local.md last. Concatenation, not override. So a fleet-wide CLAUDE.md at ~/.claude/CLAUDE.md gets concatenated with <project>/.claude/CLAUDE.md and <project>/CLAUDE.md automatically — no explicit imports needed if the path tree is right.

Subagents can preload skills via the skills: frontmatter field (https://code.claude.com/docs/en/sub-agents#preload-skills-into-subagents):

“The full content of each listed skill is injected into the subagent’s context at startup.”

This is “explicit inheritance” — the subagent declares which shared knowledge packets it wants at boot, by name.

.claude/rules/ is a third compositional layer for path-scoped instructions:

your-project/
├── .claude/
│ ├── CLAUDE.md
│ └── rules/
│ ├── code-style.md
│ ├── testing.md # frontmatter: paths: ["**/*.test.ts"]
│ └── security.md

Rules with no paths: frontmatter load always; rules with paths: load only when Claude reads matching files. Same priority as .claude/CLAUDE.md. Source: https://code.claude.com/docs/en/memory#organize-rules-with-claude/rules/.

Claude Code has three distinct storage tiers with explicit boundaries:

TierLocationWhat it’s forSurvives
Project memory<repo>/CLAUDE.md, .claude/rules/*.mdTeam-shared standardsGit
User memory~/.claude/CLAUDE.md, ~/.claude/rules/*.mdPersonal prefs across projectsJust user
Auto memory (per-project)~/.claude/projects/<project>/memory/MEMORY.mdClaude-written learnings, repo-scopedLocal only
Subagent memory~/.claude/agent-memory/<name>/ (user), .claude/agent-memory/<name>/ (project), .claude/agent-memory-local/<name>/ (local)Agent’s own accumulated knowledgePer scope
Session transcript~/.claude/projects/<project>/<sessionId>/subagents/agent-<agentId>.jsonlSubagent conversation history30 days default

Quote on the boundary (https://code.claude.com/docs/en/sub-agents#enable-persistent-memory):

project is the recommended default scope. It makes subagent knowledge shareable via version control. Use user when the subagent’s knowledge is broadly applicable across projects, or local when the knowledge should not be checked into version control.”

MEMORY.md is treated as an index — first 200 lines / 25KB load at session start, topic files load on demand. Topic files (debugging.md, api-conventions.md, etc.) are read by the agent when needed using standard file tools. There’s no separate vector store or RAG layer in the default architecture — it’s just filesystem + structured loading rules.

Synthesized from the docs:

<project-root>/
├── CLAUDE.md # project context (alt: .claude/CLAUDE.md)
├── CLAUDE.local.md # gitignored personal overrides
├── .claude/
│ ├── CLAUDE.md # alternative location for project memory
│ ├── settings.json # technical enforcement (committed)
│ ├── settings.local.json # local overrides (gitignored)
│ ├── agents/
│ │ ├── code-reviewer.md # named subagent
│ │ ├── debugger.md
│ │ └── researcher.md
│ ├── skills/
│ │ ├── deploy/
│ │ │ ├── SKILL.md # always required
│ │ │ ├── scripts/
│ │ │ └── references/
│ │ └── pdf-processing/
│ │ └── SKILL.md
│ ├── commands/ # legacy; merged into skills
│ ├── rules/
│ │ ├── code-style.md
│ │ ├── testing.md # frontmatter: paths: ["**/*.test.ts"]
│ │ └── security.md
│ ├── hooks/
│ ├── agent-memory/ # project-scope, committed
│ │ └── code-reviewer/
│ │ └── MEMORY.md
│ └── agent-memory-local/ # local-only, gitignored
└── .mcp.json # MCP server configs
~/.claude/ # user-level scope
├── CLAUDE.md
├── settings.json
├── agents/
├── skills/
├── rules/
├── output-styles/
├── projects/<project>/memory/ # auto memory per repo
│ ├── MEMORY.md
│ ├── debugging.md
│ └── api-conventions.md
├── agent-memory/<agent-name>/ # user-scope subagent memory
└── plugins/
<plugin>/
├── .claude-plugin/
│ └── plugin.json # ONLY plugin.json here
├── skills/<name>/SKILL.md
├── agents/
├── commands/ # legacy flat .md files
├── hooks/hooks.json
├── monitors/monitors.json
├── .mcp.json
├── .lsp.json
├── bin/
└── settings.json

Source: https://code.claude.com/docs/en/plugins. Explicit warning:

“Common mistake: Don’t put commands/, agents/, skills/, or hooks/ inside the .claude-plugin/ directory. Only plugin.json goes inside .claude-plugin/. All other directories must be at the plugin root level.”


2. Anthropic Claude Agent SDK (Python + TypeScript)

Section titled “2. Anthropic Claude Agent SDK (Python + TypeScript)”

The SDK is “Claude Code as a library.” It does not invent its own layout — it reads Claude Code’s filesystem conventions. Same files, same hierarchy, but you can also pass config programmatically.

Source: https://code.claude.com/docs/en/agent-sdk/overview

Two paths:

Filesystem (preferred for persistent agents): Same as Claude Code — CLAUDE.md, .claude/agents/*.md, .claude/skills/*/SKILL.md.

“The SDK also supports Claude Code’s filesystem-based configuration. With default options the SDK loads these from .claude/ in your working directory and ~/.claude/. To restrict which sources load, set setting_sources (Python) or settingSources (TypeScript) in your options.”

Mapping table (verbatim from the overview):

FeatureLocation
Skills.claude/skills/*/SKILL.md
Slash commands.claude/commands/*.md
MemoryCLAUDE.md or .claude/CLAUDE.md
PluginsProgrammatic via plugins option

Programmatic (for ephemeral / per-call agents):

# from code.claude.com/docs/en/agent-sdk/overview
agents={
"code-reviewer": AgentDefinition(
description="Expert code reviewer for quality and security reviews.",
prompt="Analyze code quality and suggest improvements.",
tools=["Read", "Glob", "Grep"],
)
}

AgentDefinition takes a prompt (the persona body), description, and tools. Identical shape to the on-disk frontmatter + body, just in code.

ClaudeAgentOptions (Python) / options object (TypeScript) carries the runtime config:

ClaudeAgentOptions(
cwd="/path/to/project",
setting_sources=["user", "project"], # which filesystem layers to load
skills="all", # or ["pdf", "docx"] or []
allowed_tools=["Read", "Write", "Bash"],
disallowed_tools=...,
permission_mode="acceptEdits",
hooks={"PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[...])]},
mcp_servers={"playwright": {"command": "npx", "args": [...]}},
system_prompt={"type": "preset", "preset": "claude_code", "append": "..."},
resume=session_id,
agents={...},
)

For persistent named agents on disk, runtime config lives in the agent file’s frontmatter (same as Claude Code). For programmatic spawns, it lives in code.

For filesystem agents: same file (frontmatter + body). For programmatic agents: passed inline as code; no file required.

The SDK explicitly does not provide a programmatic API for skills:

“Unlike subagents (which can be defined programmatically), Skills must be created as filesystem artifacts. The SDK does not provide a programmatic API for registering Skills.” — code.claude.com/docs/en/agent-sdk/skills

Three composition mechanisms via system_prompt (https://code.claude.com/docs/en/agent-sdk/modifying-system-prompts):

# 1. Minimal default (no preset) — SDK uses tiny system prompt
ClaudeAgentOptions()
# 2. Claude Code preset (full system prompt + tools + env context)
ClaudeAgentOptions(
system_prompt={"type": "preset", "preset": "claude_code"}
)
# 3. Preset + append (preserves built-ins, adds custom)
ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "Always include detailed docstrings."
}
)
# 4. Full custom string (replaces everything)
ClaudeAgentOptions(system_prompt="You are a Python specialist...")

Plus the on-disk @-imports and CLAUDE.md cascade still work when setting_sources includes project and user.

Cache-friendly fleet pattern: excludeDynamicSections: true makes the preset static (moves cwd/git/date into the first user message) so a fleet of agents in different directories can share a cached system prompt:

ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "You operate Acme's internal triage workflow...",
"exclude_dynamic_sections": True,
}
)

Tradeoff named in the docs: “Instructions in the user message carry marginally less weight than the same text in the system prompt.”

Filesystem-driven, same tiers as Claude Code (Section 1.5). The SDK also exposes session state explicitly:

“Session state: JSONL on your filesystem” (per the Agent SDK vs Managed Agents comparison).

Sessions can be resumed by ID:

ClaudeAgentOptions(resume=session_id)

Identical to Claude Code (Section 1.6). The SDK is the same harness with a programmatic entry point.


3. Agent Skills (open standard — agentskills.io)

Section titled “3. Agent Skills (open standard — agentskills.io)”

Skills are the atomic unit of shared capability — a portable folder of instructions + assets that any compatible agent can load.

Source: https://agentskills.io/specification

3.1 Identity / runtime config / shared knowledge / folder

Section titled “3.1 Identity / runtime config / shared knowledge / folder”

Skills don’t have a “persona” — they’re capabilities, not agents. But the SKILL.md frontmatter answers the same design questions for capability packaging:

my-skill/
├── SKILL.md # Required: metadata + instructions
├── scripts/ # Optional: executable code
├── references/ # Optional: documentation
├── assets/ # Optional: templates, resources
└── ...

SKILL.md frontmatter spec (verbatim from agentskills.io/specification):

FieldRequiredConstraints
nameYesMax 64 chars; lowercase + hyphens only; no leading/trailing/consecutive hyphens; must match parent directory name
descriptionYesMax 1024 chars; what it does + when to use
licenseNoLicense name or bundled file ref
compatibilityNoMax 500 chars; environment requirements
metadataNoArbitrary key-value mapping
allowed-toolsNoSpace-separated pre-approved tools (experimental)

The body is freeform Markdown. Recommended sections: step-by-step instructions, examples, common edge cases.

Progressive disclosure is the load model (verbatim):

“1. Metadata (~100 tokens): The name and description fields are loaded at startup for all skills 2. Instructions (< 5000 tokens recommended): The full SKILL.md body is loaded when the skill is activated 3. Resources (as needed): Files (e.g. those in scripts/, references/, or assets/) are loaded only when required

Keep your main SKILL.md under 500 lines. Move detailed reference material to separate files.”

This is the same three-tier “preview → activate → expand” pattern used by Claude Code skills internally.

Adoption: 30+ clients listed at agentskills.io including Claude Code, Claude.ai, Gemini CLI, Cursor, OpenAI Codex, GitHub Copilot, VS Code, Goose, OpenHands, Letta, Roo Code, Mistral Vibe. Originally developed by Anthropic, now an open standard.


4. OpenAI Assistants API (deprecated) / Responses API + Prompts

Section titled “4. OpenAI Assistants API (deprecated) / Responses API + Prompts”

Important status: The Assistants API is deprecated, shutdown August 26, 2026, after feature parity with the Responses API. Don’t build new integrations on it. Source: https://developers.openai.com/api/docs/assistants/migration

I report both because the design tradeoffs are still instructive.

Assistants API (legacy): persona lives in a server-stored object created via API. Not a file. Schema:

{
"name": "...",
"instructions": "...", // up to 256,000 chars — the persona/system prompt
"tools": [...],
"model": "gpt-4o",
"tool_resources": {...}, // file_search, code_interpreter resources
"metadata": {...}
}

Source: search result via developers.openai.com (legacy schema).

Persistence model is database-as-source-of-truth: the assistant lives in OpenAI’s backend, identified by an opaque ID. No filesystem convention.

Responses API + Prompts (current):

“Prompts can only be created in the dashboard, where you can version them as you develop your product. Using prompts, every Responses or Realtime session you start inherits a consistent contract because prompts encapsulate tool schemas and structured output expectations.”

Persona is now a versioned dashboard artifact, referenced by stable ID at runtime. Still not a file in your repo — it’s in OpenAI’s dashboard.

Same object. Tools, model, tool_resources, and instructions are all fields on the assistant (or prompt). Nothing is split across files.

There is no file. Everything is one server-side object. The migration guide explicitly frames this as the problem they’re solving:

“Assistants bundled configuration into API objects, while Responses API separates concerns.”

Post-migration: prompts handle the “behavioral profile”; conversations handle the streamed message/tool-call state; application code handles orchestration. Three concerns, three places — but none of those places are files in your repo.

No native @-import or inheritance. The 256k-char instruction field is flat text. If you want shared ethos across many assistants, you concatenate it yourself in code or in the dashboard.

The Responses API improves this slightly with versioned prompts that can be referenced by ID — so you can have one “shared ethos prompt” and other specialist prompts in the dashboard — but the composition is still done at the application layer, not declaratively.

  • Shared knowledge: tool_resources.file_search attaches files to a vector store; the assistant retrieves on demand. This is the canonical RAG path. Files live in OpenAI’s storage, not your repo.
  • Per-thread state: Conversations/threads store messages and tool calls server-side, retrievable by ID.
  • Local state: Nothing. Your application owns any local cache.

The docs don’t recommend one. There is no “OpenAI Assistant project layout” guide — assistants are server-side objects, so the layout is whatever your application code wants. The Assistants API deep-dive page treats this as a code-organization-is-your-problem matter.


Source: https://openai.github.io/openai-agents-python/

In code, as a Python object. No filesystem convention.

from agents import Agent, function_tool
@function_tool
def get_weather(city: str) -> str:
return f"The weather in {city} is sunny"
agent = Agent(
name="Weather Assistant",
instructions="Help with weather queries",
model="gpt-4",
tools=[get_weather]
)

Agent parameters (from the Agents page):

ParameterRequiredPurpose
nameYesHuman-readable identifier
instructionsYesSystem prompt (string or callable)
modelNoWhich LLM to use
toolsNoCapabilities the agent can invoke
handoffsNoSpecialist agents to delegate to
output_typeNoStructured output format
model_settingsNoTemperature, etc.
input_guardrails / output_guardrailsNoValidation checks

instructions can be a callable, not just a string:

def dynamic_instructions(context, agent) -> str:
return f"The user's name is {context.context.name}..."
agent = Agent(instructions=dynamic_instructions)

This is the only framework studied where the persona is allowed to be a function of runtime context.

Same Python object. Everything is constructor args. No frontmatter, no config files.

There is no file. Identity and config are one Python object, instantiated in code.

No declarative composition. Two patterns recommended in the docs:

  • Agents as Tools: A manager agent calls specialist agents via Agent.as_tool().
  • Handoffs: A triage agent routes to specialists who become active.

“Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions.”

Translation: there is no inheritance system. If you want shared ethos, you compose Python strings, share a Python module of common instruction text, or wrap Agent(...) calls in a factory function. The SDK is explicitly Python-first, no DSL.

  • Context: A typed object passed to Runner.run(), threaded through every agent, tool, and handoff. Dependency injection pattern. Agents are generic on context type: Agent[UserContext](...).
  • Sessions: “A persistent memory layer for maintaining working context within an agent loop.”

No filesystem layer. State is whatever your Python objects hold.

The docs do not specify one. From WebFetch summary:

“The OpenAI Agents SDK does not specify a recommended file/folder layout. The documentation focuses on functional capabilities rather than project structure conventions. The framework prioritizes flexibility over prescriptive architecture, allowing developers to organize code according to their specific needs.”

This is a deliberate non-decision. It cuts both ways: full freedom for the developer, zero guidance on how to scale to 18 agents.


6. Comparison Table — Framework × Design Question

Section titled “6. Comparison Table — Framework × Design Question”
QuestionClaude CodeClaude Agent SDKAgent Skills (open)OpenAI AssistantsOpenAI Agents SDK
Persona lives in.md file body (subagent) or CLAUDE.md (main session)Same file as Claude Code, OR AgentDefinition.prompt in codeN/A (skills are capabilities, not personas)instructions field on server-stored objectinstructions param on Agent(...) Python object
Runtime config lives inYAML frontmatter (same file) + settings.json (separate, for enforcement)Frontmatter for file agents; ClaudeAgentOptions(...) for programmaticSKILL.md frontmatterSame server-stored objectSame Agent(...) constructor
Same file or split?Persona + config: same file. Enforcement settings: separate fileSame file for filesystem agents; one object for code agentsSame file (frontmatter + body)Single object, no filesSingle Python object
Composition@path imports (max 5 hops) + directory cascade (concatenate root→cwd) + skills: preload + .claude/rules/Same filesystem mechanisms + system_prompt.append + presetsNone (skill = atomic unit)None native; concatenate in app codeNone native; Python factory functions
Shared knowledge vs agent-local5 tiers: project CLAUDE.md (git), user CLAUDE.md (machine), auto-memory (per-repo, local), subagent memory (user/project/local scopes), session jsonlSame 5 tiersSkill body + references/ + assets/Vector store via tool_resources.file_search; threads for stateTyped context object, Sessions for working memory
Folder structure.claude/{agents,skills,rules,commands,hooks,agent-memory}/ + CLAUDE.md + .mcp.json + settings.jsonSame as Claude Code<skill-name>/{SKILL.md, scripts/, references/, assets/}Not specifiedNot specified
Identity precedencemanaged > CLI flag > project > user > pluginSameN/AN/AN/A
DiscoverabilityWalk dir tree; load CLAUDE.md/rules at start; skills load metadata at start, body on activationSameTwo-stage progressive disclosureServer lists assistants by IDImports list in Python

#1 — Co-locate persona + runtime config in one file, separate enforcement settings

Section titled “#1 — Co-locate persona + runtime config in one file, separate enforcement settings”

Why: Both Claude Code (for subagents) and Agent Skills do this. Frontmatter holds machine-readable config; body holds human-readable persona/ instructions. One file = one identity, atomically version-controlled, editable as a single unit. Enforcement settings (deny rules, sandbox, hooks) live separately because they must be deterministic and outside the model’s ability to argue with.

Concrete: <name>.md with YAML frontmatter for model/tools/hooks/memory and Markdown body for persona/system prompt. settings.json next to it for permissions/env.

#2 — @path imports with cascade (root → cwd) for shared ethos

Section titled “#2 — @path imports with cascade (root → cwd) for shared ethos”

Why: Claude Code’s only declarative inheritance mechanism, and the one already in production use here. Max 5 hops, relative-to-file, supports ~/. Combined with the directory walk (root-most loads first, concatenated), this lets a fleet doctrine file at ~/.claude/docs/fleet- ethos.md be @-imported by every agent’s CLAUDE.md.

Antidote to: per-agent copy-pasted ethos that goes stale unevenly.

#3 — Progressive-disclosure skill packaging (SKILL.md + references/)

Section titled “#3 — Progressive-disclosure skill packaging (SKILL.md + references/)”

Why: Industry standard now (Anthropic + 30+ adopters). Forces you to write a tight description (~100 tokens) that loads at start, a medium-length body (<5000 tokens) that loads on activation, and detailed references that load on demand. Keeps context budget small per agent while still allowing deep specialization. Naming convention enforces folder-name-equals-skill-name discipline.

#4 — Tiered memory: shared (project) / personal (user) / scratch (local)

Section titled “#4 — Tiered memory: shared (project) / personal (user) / scratch (local)”

Why: Claude Code’s three-scope memory (project / user / local) maps cleanly to “team canon, personal across projects, sandbox notes.” Each scope has explicit git semantics: project is committed, user is per- machine, local is gitignored. The same trichotomy applies to subagent memory and rules.

#5 — Agent definitions discoverable by walking the filesystem

Section titled “#5 — Agent definitions discoverable by walking the filesystem”

Why: No registry, no database, no central manifest. The agent harness walks ~/.claude/agents/ and .claude/agents/, reads frontmatter, and that’s the catalog. New agent = drop a file. Deprecated agent = delete the file. Beats every database-of-assistants pattern for ops simplicity, especially across 5 machines where sync is filesystem-based anyway.

#6 — Frontmatter paths: globs for path-scoped instructions

Section titled “#6 — Frontmatter paths: globs for path-scoped instructions”

Why: .claude/rules/*.md with paths: ["src/api/**/*.ts"] means the rule only enters context when relevant files are being read. Prevents the “every CLAUDE.md gets fatter forever” failure mode. Direct analog: specialty workers loading their domain pack only when invoked.

#7 — excludeDynamicSections: true for cross-machine cache reuse

Section titled “#7 — excludeDynamicSections: true for cross-machine cache reuse”

Why: Specifically called out for fleet patterns — if you have many agents that share an append block but run from different cwds, this hoists per-session context (cwd, git, date) into the first user message so the system prompt stays identical across machines. Real cost savings on prompt cache hits. The tradeoff (slightly less authoritative env context) is documented.

#8 — Callable instructions (OpenAI Agents SDK)

Section titled “#8 — Callable instructions (OpenAI Agents SDK)”

Why: Only framework where instructions can be a function of context. Useful for personalization or for switching tone based on who’s calling. Stealable as a pattern even if your harness is filesystem-based: a tiny templating layer over CLAUDE.md before injection.


  • Don’t put commands/, agents/, skills/, hooks/ inside .claude-plugin/. Only plugin.json goes there. Source: https://code.claude.com/docs/en/plugins#plugin-structure-overview.
  • Don’t oversize CLAUDE.md. “Target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence.” Auto-memory has the same 200-line/25KB cap on MEMORY.md.
  • Don’t expect imports to reduce context. “Splitting into @path imports helps organization but does not reduce context, since imported files load at launch.”
  • Don’t put long multi-step procedures in CLAUDE.md. “If an entry is a multi-step procedure or only matters for one part of the codebase, move it to a skill or a path-scoped rule instead.”
  • Don’t expect contradictions to resolve. “If two rules contradict each other, Claude may pick one arbitrarily.” Implicit: declare a canonical doctrine file and prune contradictions.
  • Don’t mistake CLAUDE.md for enforcement. “CLAUDE.md content is delivered as a user message after the system prompt, not as part of the system prompt itself. Claude reads it and tries to follow it, but there’s no guarantee of strict compliance.” Use hooks or permissions for hard rules.
  • Don’t put --add-dir directories’ CLAUDE.md and agents/ in the load path by default. Skills are an explicit exception; subagents, commands, and output styles are not loaded from added dirs.
  • Don’t deeply nest references. “Keep file references one level deep from SKILL.md. Avoid deeply nested reference chains.”
  • Don’t write vague descriptions. Spec gives a “poor example: description: Helps with PDFs.” The good version includes specific keywords (PDFs, forms, document extraction) so the matcher can find it.
  • Skill name must match parent directory name. Hard validation rule.
  • Default minimal preset omits Claude Code’s behavioral guidelines. If you want the full Claude Code system prompt (tool style, response tone, safety), you must opt in: system_prompt={"type": "preset", "preset": "claude_code"}. Easy to forget.
  • Custom string system_prompt loses default tools and safety instructions. “Built-in safety: Must be added.”
  • setting_sources=[] disables filesystem loading entirely. Skills, CLAUDE.md, output styles all vanish. Recurring gotcha called out in the skills doc.
  • Subagents cannot spawn other subagents. No nested delegation. Workaround: chain subagents from the main conversation, or use skills.
  • The Assistants API itself is now an antipattern. Shut down 2026-08-26. Don’t start new builds on it.
  • No native composition/inheritance. A flat 256k-char instructions field across many assistants creates copy-paste drift; the migration guide acknowledges this as motivation for the Prompts approach.
  • Server-stored personas can’t be diffed in git. The migration guide explicitly cites “portability and versioning” as the new design goal because the old one didn’t have it.
  • No prescribed folder layout. This is a non-decision that punts the hard problem (organizing many agents) to the developer. For a fleet of 18 agents across 5 machines, this means inventing your own structure.
  • No declarative shared-ethos mechanism. Composition is “use Python features” — fine for code, weaker for declarative team-shared rules. Every agent’s instructions string is independent text.

  • Claude Code: Identity and runtime config in one .md file with YAML frontmatter; enforcement in separate settings.json. Shared ethos via @path imports (max 5 hops) and directory-walk cascade (root→cwd, concatenate). Three memory tiers (project/user/local). Subagents preload skills by name. Plugins package the same primitives for distribution. Filesystem is the database.
  • Claude Agent SDK: Same conventions as Claude Code (it’s the same harness as a library), plus programmatic AgentDefinition for ephemeral agents and system_prompt={preset, append, exclude_dynamic_ sections} for cache-friendly fleets. setting_sources decides which filesystem layers load.
  • Agent Skills (open standard): Atomic capability folder with required SKILL.md (YAML frontmatter + Markdown), optional scripts/, references/, assets/. Three-tier progressive disclosure (metadata → body → resources). 30+ vendor adoptees. Name must match parent directory.
  • OpenAI Assistants API: Deprecated; sunset 2026-08-26. Single server-stored object holds name, instructions, tools, model. No filesystem, no inheritance, no declarative composition. Replaced by Responses API + Prompts, which separates the behavioral profile from conversation state and orchestration.
  • OpenAI Agents SDK: Python-first; agent is a constructor call (Agent(name, instructions, model, tools, handoffs, ...)); no prescribed folder layout; composition via Agents-as-Tools or Handoffs patterns at the code level; instructions can be a callable (dynamic, context-aware persona).