| name | harness-conventions |
| description | Project conventions for the Ollama Agent Harness — architectural patterns borrowed from the Claude Code paper |
| domain | project-conventions |
| confidence | medium |
| source | generated by CopilotForge |
Context
This skill defines conventions for the Ollama Agent Harness project. The architecture borrows from the design patterns analyzed in "Dive into Claude Code: The Design Space of Today's and Future AI Agent Systems" (Liu et al., 2026). The harness wraps Ollama's local API to provide an agentic coding tool with tool dispatch, permission gating, context management, subagent delegation, and session persistence.
Patterns
Minimal Scaffolding, Maximal Operational Harness
The core agent loop is a simple while-loop that calls the model, runs tools, and repeats. Most code lives in the systems around this loop: permission checks, context management, tool routing, and recovery logic. Do not add explicit planning graphs or state machines to constrain the model's reasoning.
Deny-First Safety Posture
Unrecognized tool actions default to deny or ask. Deny rules override allow rules even when the allow rule is more specific. Permission evaluation runs before every tool dispatch.
Context as Scarce Resource
Ollama models have bounded context windows. Apply progressive context management: budget reduction first, then snip, then summarization. Cheaper strategies run before costlier ones.
Append-Only Session State
Session transcripts are append-only JSONL files. Compaction appends summary events; it never modifies or deletes prior lines. This preserves auditability and supports resume/fork operations.
Tool Dispatch Classification
Tools are classified as concurrent-safe (read-only) or exclusive (state-modifying). Read-only tools can execute in parallel; state-modifying tools are serialized.
Symbol Lookups Prefer code_graph Over Grep
For "who calls X", "what does X call", "what does this file export", or "show the neighbourhood around X" questions, use the code_graph tool first. It reads a static index of src/ (built from the TypeScript compiler API) and returns precise file/line answers — no false positives from comments or strings, and aliased re-exports are followed.
Operations:
code_graph callers <symbol> — incoming calls
code_graph callees <symbol> — outgoing calls
code_graph exports <fragment> — what a file exports
code_graph around <symbol> [depth] — local subgraph
code_graph stats — top-called functions
Fall back to grep only when the question is text-shaped (string literals, comments, config files, log messages) or when code_graph reports the graph file is missing — in which case run node scripts/build-code-graph.js and retry. The graph file lives at .harness/code-graph.json and is rebuilt manually for now (no auto-rebuild).
Subagent Isolation
Subagents operate in isolated context windows. Only summary text returns to the parent — never the full conversation history. This prevents context explosion.
TypeScript Conventions
- Use strict mode (
"strict": true in tsconfig.json)
- Prefer
async/await and async generators over callbacks
- Use explicit type annotations for function signatures
- Organize source under
src/ with clear subsystem directories
- Use path aliases for cleaner imports
File Organization
src/
core/ # Agent loop, query pipeline
tools/ # Tool implementations and dispatch
permissions/ # Permission rules and evaluation
context/ # Context assembly and compaction
agents/ # Subagent delegation
persistence/ # Session storage and recovery
extensibility/ # Skills, hooks, plugin loading
types/ # Shared type definitions
Ollama Integration
- Use the official
ollama npm package for API communication
- Support streaming responses via async iterators
- Handle tool calls from the
message.tool_calls array
- Maintain message history in the OpenAI-compatible format Ollama uses
Examples
Agent Loop Pattern (ReAct)
async function* queryLoop(config: QueryConfig): AsyncGenerator<LoopEvent> {
while (!stopped) {
const context = assembleContext(config);
const response = await callModel(context);
if (!response.toolCalls?.length) {
yield { type: 'text', content: response.content };
break;
}
for (const call of response.toolCalls) {
const permitted = await checkPermission(call);
if (!permitted) continue;
const result = await dispatchTool(call);
yield { type: 'tool_result', call, result };
}
}
}
Tool Definition
interface Tool {
name: string;
description: string;
parameters: JsonSchema;
isReadOnly: boolean;
execute(input: unknown): Promise<ToolResult>;
}
Anti-Patterns
- Adding explicit state graphs or planning frameworks that constrain model reasoning — use the minimal scaffolding approach instead.
- Sharing full conversation history between parent and subagent — return summary only.
- Mutating session transcript files in place — always append.
- Running all tools serially when some are read-only and can safely parallelize.
- Hardcoding Ollama model names — make them configurable.
- Ignoring tool call errors silently — surface them as tool results so the model can adapt.