Chapter 10: Prompt Construction and Project Memory
Prompt Engineering As Runtime Design
In a coding agent, the system prompt is not just prose. It is a runtime contract. It tells the model:
- What role it is playing.
- How to use tools.
- How to respect permissions and sandbox constraints.
- How to communicate progress.
- How to interpret project instructions.
- How to finish work and report results.
The prompt is assembled from stable instructions, dynamic environment facts, tool schemas, project memory, and sometimes user-specific or mode-specific sections.
A Prompt Builder Skeleton
def build_agent_prompt(base, environment, project, tools, mode):
sections = []
sections.append(base.identity_and_behavior)
sections.append(base.tool_usage_rules)
sections.append(render_mode_instructions(mode))
sections.append(render_environment(environment))
sections.append(render_project_instructions(project))
sections.append(render_available_tools(tools))
return "\n\n".join(section for section in sections if section)
Good prompt builders separate stable content from dynamic content. Stable content is easier to cache and reason about. Dynamic content changes per workspace, turn, or tool set.
Codex: Compiled Base Instructions Plus Runtime Context
Codex embeds a strong base instruction document into the binary. Around that base, the runtime adds environment context, configuration, active tools, skills, connectors, collaboration mode, sandbox/approval constraints, and user input.
Codex Prompt Shape
def build_codex_prompt(turn):
instructions = []
instructions.append(load_compiled_base_instructions())
instructions.append(render_developer_overrides(turn.config))
instructions.append(render_sandbox_and_approval_context(turn))
instructions.append(render_project_instructions(turn.workspace))
instructions.append(render_skills_and_plugins(turn))
instructions.append(render_user_environment(turn))
return {
"instructions": join_sections(instructions),
"input": turn.history.for_prompt(turn.model),
"tools": turn.tool_router.model_visible_tools(),
"tool_choice": "auto",
}
Codex keeps many tool details in model-visible schemas rather than repeating large tool manuals in the system prompt. The base instructions focus on agent behavior: be concise, use tools, validate changes, avoid destructive actions, respect user changes, and report final results clearly.
Claw: Modular Claude-Style Prompt Sections
Claw builds the system prompt from modular sections. The builder includes an intro, output style, system behavior, task guidance, action rules, a dynamic boundary, environment details, project memory files, config-derived sections, and appended custom sections.
Claw Prompt Shape
def build_claw_prompt(config, workspace):
builder = SystemPromptBuilder()
builder.add_intro(model_label=config.model_label)
builder.add_output_style(config.output_style)
builder.add_system_rules(config.system_rules)
builder.add_task_guidance()
builder.add_action_guidance()
builder.add_cache_boundary("__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__")
builder.add_environment(current_environment())
builder.add_project_memory(load_project_memory(workspace))
builder.add_config_sections(config.prompt_sections)
builder.add_append_sections(config.appended_prompt_text)
return builder.render()
The explicit boundary matters because Claude-style providers can benefit from prompt caching. Stable instructions should appear before volatile environment and project data when possible.
Project Instructions
Project instructions are one of the highest-value context sources. They turn a generic coding agent into a repository-aware agent.
def load_project_instructions(workspace):
candidates = [
"AGENTS.md",
"CLAUDE.md",
"CLAUDE.local.md",
".claw/CLAUDE.md",
".claw/instructions.md",
]
instructions = []
for name in candidates:
file = workspace.find(name)
if file.exists():
instructions.append(read_text(file))
return "\n\n".join(instructions)
Codex emphasizes AGENTS.md style repository guidance. Claw loads Claude-style
memory files and config sections. The principle is the same: local instructions
should override generic behavior when they are relevant and safe.
Tool Instructions
There are two ways to teach tool use:
| Method | Description | Tradeoff |
|---|---|---|
| Tool schema | Put name, description, and JSON schema in the API tools field |
Structured and compact, but less narrative |
| Prompt text | Add prose explaining when and how to use tools | More guidance, but consumes context |
Codex leans toward schemas plus compact base behavior. Claw has many named tools and can include richer tool-specific guidance through prompt sections and tool definitions.
def describe_tool_for_model(tool):
return {
"name": tool.name,
"description": tool.short_description,
"input_schema": tool.input_schema,
}
The best prompt does not explain every tool in long prose. It gives the model enough to choose correctly, then relies on schemas and runtime errors for precision.
Dynamic Context
Dynamic context includes facts that can change every turn:
- Current date and environment.
- Working directory.
- Git status or base commit warnings.
- Active sandbox and approval mode.
- Available MCP servers or resources.
- Current plan, goal, or todo list.
- Active sub-agents.
def render_environment_context(env):
return f"""
Current date: {env.date}
Working directory: {env.cwd}
Sandbox mode: {env.sandbox_mode}
Approval mode: {env.approval_mode}
Network: {env.network_policy}
"""
Dynamic context should be factual and short. It should not bury the model in implementation details.
Configuration, Plugins, MCP, and Skills in the Prompt
Extensions are useful only if the model can discover when to use them. The prompt should include just enough extension context to guide tool choice without turning the system prompt into a catalog.
def render_extension_prompt_context(config, plugins, mcp_servers, skills):
sections = []
if config.active_profile:
sections.append(f"Active profile: {config.active_profile}")
if plugins:
sections.append(render_plugin_summaries(plugins))
if mcp_servers:
sections.append(render_mcp_server_summaries(mcp_servers))
if skills:
sections.append(render_skill_summaries(skills))
return "\n\n".join(sections)
The model does not need long implementation paths or full plugin manifests. It needs concise operational facts:
- Which profile or mode is active.
- Which external servers or resources are connected.
- Which skills are available for the current task.
- Which extension tools require approval or have constraints.
Codex tends to keep many details in schemas and runtime context. Claw's modular prompt builder can add config-derived sections, skill summaries, and MCP context near the dynamic part of the prompt.
Prompt Quality Rules
Good agent prompts share several properties:
- Stable identity and safety rules come first.
- Repository instructions are included but not repeated unnecessarily.
- Tool schemas are authoritative for arguments.
- Dynamic environment facts are explicit.
- The model is told how to communicate and when to stop.
- The prompt avoids long source paths or internal implementation dumps.
Comparison
| Aspect | Codex | Claw |
|---|---|---|
| Base prompt | Compiled into the binary | Built by a runtime prompt builder |
| Dynamic sections | Turn/environment/config context | Environment, project memory, config sections |
| Project memory | Repository instruction files | Claude-style memory files and .claw instructions |
| Tool guidance | Strong schemas plus base behavior | Broad tool surface with modular prompt guidance |
| Cache strategy | Provider cache key and stable request state | Explicit dynamic boundary for cache-aware prompts |
| Style | Concise, operational agent instructions | Claude-style layered instructions |
Pseudocode: Prompt Assembly With Caching
def assemble_cache_aware_prompt(stable_sections, dynamic_sections):
stable_prefix = "\n\n".join(stable_sections)
dynamic_suffix = "\n\n".join(dynamic_sections)
return {
"cacheable_prefix": stable_prefix,
"dynamic_suffix": dynamic_suffix,
"full_prompt": stable_prefix + "\n\n" + dynamic_suffix,
}
The cache boundary should not be a cosmetic marker. It should reflect which content is stable across turns and which content changes frequently.
Source Anchors
For Codex, useful filenames are default.md, models.rs, and turn.rs. For
Claw, useful filenames are prompt.rs, conversation.rs, and the config-loading
modules.