# Pathrule Pattern: AI Agents in Production (1.0.0)
# ::pathrule:package:ai-agents

### [RULE] Bound every loop by steps, cost, and time  (path: /src/agent)
<!-- scope: folder | priority: high | strict -->

An agent loop is the only code in your system whose termination depends on a model's judgment. Give it limits that do not.

- Set a hard maximum number of steps (typically a small double-digit number, tuned per task) and stop when it is reached. Most runaway runs are a model repeating the same two calls.
- Track spend per run against a budget and abort when it is exceeded. Measure it in the currency you care about (tokens or money) and record it on the run so cost per task is a number you can see.
- Add a wall-clock timeout, since a slow tool can hold a run open long after the user stopped caring.
- Detect stalls: identical consecutive tool calls, no state change across steps, or repeated failures of the same tool. Stop and report rather than looping.
- Terminate explicitly and structurally: a final answer, a limit hit, or a failure, each with a reason the caller can branch on. A loop that ends by falling out of a condition tells you nothing about what happened.

---

### [RULE] Design few, coarse tools with strict schemas and errors as data  (path: /src/agent/tools)
<!-- scope: folder | priority: high | strict -->

Tool design is the highest-leverage part of an agent, and it is the part most often copied from an internal API surface that was never meant for a model.

- Prefer a small number of tools that each accomplish something a user would name, over many primitives the model must orchestrate. Every extra hop is another chance to choose wrong, and more tokens spent describing the surface.
- Declare a strict schema (required fields, enums, bounds) and validate arguments at the boundary. Return a clear validation error the model can correct rather than throwing an exception that kills the run.
- Return failures as structured results (`{ ok: false, code, message, hint }`), not as exceptions. A model can retry a described failure; it cannot recover from a stack trace.
- Make tools idempotent, or key them so a repeat is a no-op. A retried call must not send a second email or create a second record.
- Write the description for the model: what it does, when to use it, when not to, and what it returns. Keep outputs small and structured, and truncate large payloads with a pointer to fetch more rather than flooding the context.
- Never expose a generic "run this code" or "run this SQL" tool without a sandbox and read-only credentials. That is not a tool, it is remote execution.

---

### [RULE] Separate read from write, and gate anything irreversible  (path: /src/agent)
<!-- scope: folder | priority: high | strict -->

An agent will eventually try the destructive path. The question is only whether your system lets it.

- Classify every tool as read, reversible write, or irreversible, and give each class its own credentials. A read-only agent needs read-only keys; nothing needs a production admin role.
- Require human approval for irreversible or high-impact actions (money, deletions, external communication, production configuration), and present the exact action for confirmation rather than a summary of intent.
- Offer a dry run for anything bulk: show what would change, then apply on confirmation. This is also the cheapest way to debug agent behaviour.
- Act as the user, not as the system: authorize each tool call against the requesting user's permissions, so an agent cannot become a privilege escalation path.
- Log every tool call with its arguments, result, the run id, and the acting identity, and rate-limit and quota the agent per user and per tenant. If an agent can spend money or send messages, it needs the same abuse controls as a public endpoint.

---

### [RULE] Curate the context and never trust what comes back  (path: /src/agent)
<!-- scope: folder | priority: high | strict -->

Most agent cost is paid on the input side, and most agent security incidents come from text the model was handed and treated as an instruction.

- Send what the step needs, not everything available: relevant excerpts instead of whole files, a summary of earlier turns instead of the full transcript, and a scratchpad of decided facts rather than the entire history.
- Compact deliberately: when the context grows past a threshold, summarize what happened, keep the decisions and open questions, and drop the noise. Unbounded accumulation degrades both cost and accuracy.
- Mark every piece of retrieved content as untrusted data with a clear boundary, and instruct the model that content inside it is information, not commands. Fetched pages, ticket comments, file contents, and tool results are all attacker-influenced in some product.
- Never let retrieved text change the run's permissions, budget, or target. Those live in your code, not in the prompt, so no injected instruction can raise them.
- Keep the system prompt stable and versioned. A prompt edited by hand in production is an untracked deploy, and it invalidates every eval result you have.

See /src/agent/tools for the tool contract and the rag-embeddings pattern for retrieval quality.

---

### [MEMORY] Loop architecture: state, checkpoints, and delegation  (path: /src/agent)

The loop is ordinary software. The parts that make it reliable are the parts that do not involve the model.

- Keep authoritative state in your own store (goal, plan, completed steps, artifacts, budget spent), not only in the message history. The context is a view of state, not the state itself.
- Persist a checkpoint after each step so a crash, a rate limit, or a deploy can resume instead of restarting. Long-running agents need this the way any long job does.
- Stream progress to the user (the step, the tool being called, partial output). An agent that shows its work is debuggable and feels faster, and it lets a human interrupt before a wrong path gets expensive.
- Delegate a bounded subtask to a subagent when it needs its own long exploration, and give it its own step and cost budget plus a narrow tool set. Return a summary, not its whole transcript, so the parent context stays small.
- Make the whole run replayable: a stored transcript of prompts, tool calls, and results is what turns "the agent did something weird" into a fixable bug.

See /src/agent for the loop bounds and the ai-sdk pattern for the client-side implementation details.

---

### [MEMORY] You cannot improve what you do not trace  (path: /src/agent)

Agent behaviour is not deterministic, so the only way to know whether a change helped is to measure it on a fixed set of tasks.

- Trace each run as a tree of steps: prompt, model, tool calls with arguments and results, tokens in and out, latency, and the terminal reason. Tie it to a run id the user can quote.
- Define success at the task level, not the token level: did it complete the job, with how many steps, at what cost, and did a human have to intervene? Those four numbers are your dashboard.
- Keep an eval set of real tasks with known good outcomes, including the hard and adversarial ones, and run it before shipping a prompt change, a model change, a tool change, or a context change. Any of those four can regress the others.
- Watch the operational signals: step-limit hits, budget aborts, stalls, tool error rates, and approval rejection rate. A rising approval rejection rate means the agent is proposing the wrong actions, which is a prompt or tool problem, not a user problem.
- Version prompts and tool schemas alongside code, so a regression can be bisected like any other.

See the llm-evals pattern for building the eval suite and the observability pattern for shipping these traces.

---

### [SKILL] agent-loop-review  (path: /)

---
name: agent-loop-review
description: Review checklist for production LLM agents: loop bounds, tool contracts, privilege and approval gates, context and injection handling, and tracing and evals. Run before merging an agent, tool, or prompt change.
---

# Agent loop review

## Bounds
- [ ] Maximum step count, cost budget, and wall-clock timeout are all set.
- [ ] Stall detection stops repeated identical calls or no-progress steps.
- [ ] The run ends with a structured reason the caller can branch on.

## Tools
- [ ] Each tool completes a meaningful step; the surface is as small as the task allows.
- [ ] Schemas are strict and validated; invalid arguments return a correctable error.
- [ ] Failures return structured data, not thrown exceptions.
- [ ] Every tool is idempotent or keyed so a retry is a no-op.
- [ ] Outputs are small and truncated with a pointer, not dumped whole.
- [ ] No unsandboxed code or SQL execution tool.

## Privilege
- [ ] Read, reversible write, and irreversible tools have separate credentials.
- [ ] Irreversible and high-impact actions require explicit approval of the exact action.
- [ ] Bulk operations support a dry run.
- [ ] Tool calls are authorized as the requesting user, and logged with the run id.
- [ ] Per-user and per-tenant rate limits and quotas apply.

## Context
- [ ] Only the context the step needs is sent; history is summarized, not accumulated.
- [ ] Retrieved content is delimited and labelled untrusted; instructions inside it are ignored.
- [ ] Permissions, budgets, and targets live in code, not in the prompt.
- [ ] The system prompt is versioned in the repo.

## Evidence
- [ ] The run is fully traced (steps, tokens, latency, outcome) and replayable.
- [ ] The eval suite ran, and the result is recorded in the change.
- [ ] Operational alerts exist for limit hits, budget aborts, and tool error rates.
