Resource library

QA How-To

Test AI Agent Planning Failures Step by Step

Learn to test AI agent planning failures step by step with runnable TypeScript tests for invalid, incomplete, looping, and ungrounded execution plans.

20 min read | 2,528 words

TL;DR

Test the planner and executor as separate components. Validate plan structure, dependencies, tool permissions, grounding, progress, and completion, then replay known-bad plans as deterministic Vitest cases with trace assertions.

Key Takeaways

  • Separate plan validation from plan execution so failures have a precise location.
  • Represent plans as typed data and reject unknown tools, missing dependencies, cycles, and unsafe arguments.
  • Use deterministic fixture plans before adding nondeterministic model-generated cases.
  • Verify both the final outcome and the trace of decisions that produced it.
  • Test replanning with strict budgets so an agent cannot loop indefinitely.
  • Store failing plans as regression fixtures and replay them in CI.
  • Measure planning quality with task-specific invariants, not exact wording or step order alone.

To test AI agent planning failures step by step, turn an agent plan into observable data, validate it before execution, and assert the execution trace as well as the final answer. This tutorial builds a small TypeScript harness that catches malformed plans, dependency errors, unsafe tool calls, stalled replanning, and false completion.

Planning is only one layer of agent quality. Use the AI agent testing complete guide for 2026 to place these checks beside tool, memory, safety, handoff, and outcome tests. Here, you will isolate the planning layer so a failure tells you exactly what broke.

The examples use deterministic fixtures, not a live model. That choice makes the suite fast and repeatable. After the core suite passes, you can feed model-generated plans into the same validator and save every discovered counterexample as a fixture.

What You Will Build

You will build a runnable planning test harness that can:

  • represent a multi-step agent plan as typed JSON-like data;
  • reject duplicate IDs, missing dependencies, cycles, unknown tools, and risky arguments;
  • execute valid plans through controlled fake tools and record a trace;
  • detect no-progress replanning loops and false completion;
  • replay production failures as stable Vitest regression tests.

The finished project has one implementation file and one test file. It does not call a paid API, access the network, or depend on a specific agent framework.

Prerequisites

Use Node.js 20 or newer and npm 10 or newer. Vitest 3 works with this tutorial, and the package command below selects its current compatible release through the major tag. Check your environment first:

node --version
npm --version

Create a clean project and install the test tooling:

mkdir agent-plan-tests
cd agent-plan-tests
npm init -y
npm install --save-dev typescript@5 vitest@3 @types/node
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --strict
mkdir -p src test

Add the test command to package.json manually, or run:

npm pkg set scripts.test="vitest run"
npm pkg set type="module"

Verify the setup with npm test. Vitest should start and report that no test files were found. That initial nonzero result is expected because you have not created a test yet.

TL;DR

Test layer Defect it exposes Strong assertion
Schema malformed plan every step has a unique ID, tool, and objective
Graph impossible ordering dependencies exist and the graph is acyclic
Policy unsafe intent tool and argument allowlists reject risky calls
Execution plan cannot work trace contains successful, dependency-ordered results
Progress planner stalls state changes within a bounded replan budget
Completion agent stops too early explicit task invariants are satisfied

Do not assert an exact natural-language plan when several plans can solve the task. Assert invariants: allowed tools, required evidence, valid dependencies, bounded work, and a verified completion condition.

Step 1: Define an Observable Plan Contract

Create src/planning.ts. Start with narrow types that make every planned action inspectable:

export type ToolName = 'searchDocs' | 'readTicket' | 'draftReply';

export type PlanStep = {
  id: string;
  objective: string;
  tool: ToolName;
  args: Record<string, string>;
  dependsOn: string[];
};

export type Plan = {
  goal: string;
  steps: PlanStep[];
};

export type TraceEvent = {
  stepId: string;
  tool: ToolName;
  status: 'succeeded' | 'failed';
  output?: string;
  error?: string;
};

This contract is deliberately smaller than a production agent schema. It contains the fields needed to explain planning behavior: intent, selected tool, arguments, and dependencies. Add fields such as timeout, approval class, or expected evidence only when your agent uses them.

Avoid treating chain-of-thought as the plan. Private reasoning may be unavailable, unstable, or inappropriate to log. Test the explicit action plan and externally visible decisions instead.

Verification: run npx tsc --noEmit. The command should exit without diagnostics. If TypeScript complains about module settings, confirm that package.json contains "type": "module".

Step 2: Validate Structure, Tools, and Dependencies

Append a validator to src/planning.ts:

const allowedTools = new Set<ToolName>([
  'searchDocs',
  'readTicket',
  'draftReply',
]);

export function validatePlan(plan: Plan): string[] {
  const errors: string[] = [];
  const ids = new Set<string>();

  if (!plan.goal.trim()) errors.push('goal is required');
  if (plan.steps.length === 0) errors.push('plan needs at least one step');

  for (const step of plan.steps) {
    if (!step.id.trim()) errors.push('step id is required');
    if (ids.has(step.id)) errors.push(`duplicate step id: ${step.id}`);
    ids.add(step.id);
    if (!step.objective.trim()) errors.push(`${step.id}: objective is required`);
    if (!allowedTools.has(step.tool)) errors.push(`${step.id}: unknown tool`);
  }

  for (const step of plan.steps) {
    for (const dependency of step.dependsOn) {
      if (!ids.has(dependency)) {
        errors.push(`${step.id}: missing dependency ${dependency}`);
      }
      if (dependency === step.id) {
        errors.push(`${step.id}: cannot depend on itself`);
      }
    }
  }

  return errors;
}

Create test/planning.test.ts with a valid baseline and a mutation that introduces a missing dependency:

import { describe, expect, it } from 'vitest';
import { validatePlan, type Plan } from '../src/planning.js';

const validPlan: Plan = {
  goal: 'Draft a grounded support reply',
  steps: [
    { id: 'ticket', objective: 'Read issue', tool: 'readTicket',
      args: { ticketId: 'T-42' }, dependsOn: [] },
    { id: 'docs', objective: 'Find approved fix', tool: 'searchDocs',
      args: { query: 'reset MFA' }, dependsOn: ['ticket'] },
    { id: 'reply', objective: 'Draft reply', tool: 'draftReply',
      args: {}, dependsOn: ['ticket', 'docs'] },
  ],
};

describe('validatePlan', () => {
  it('accepts a structurally valid plan', () => {
    expect(validatePlan(validPlan)).toEqual([]);
  });

  it('reports a missing dependency', () => {
    const broken = structuredClone(validPlan);
    broken.steps[2].dependsOn = ['missing'];
    expect(validatePlan(broken)).toContain(
      'reply: missing dependency missing',
    );
  });
});

Verification: run npm test. Both tests should pass. This proves that the harness distinguishes a valid fixture from an impossible reference instead of merely checking whether JSON parses.

Step 3: Detect Cycles Before Execution

A plan can reference only existing steps and still be impossible. If docs waits for reply while reply waits for docs, a naive executor hangs or continually replans. Add graph traversal inside validatePlan, immediately before return errors:

  const visiting = new Set<string>();
  const visited = new Set<string>();
  const byId = new Map(plan.steps.map((step) => [step.id, step]));

  function visit(id: string): void {
    if (visiting.has(id)) {
      errors.push(`dependency cycle includes ${id}`);
      return;
    }
    if (visited.has(id)) return;

    visiting.add(id);
    for (const dependency of byId.get(id)?.dependsOn ?? []) {
      if (byId.has(dependency)) visit(dependency);
    }
    visiting.delete(id);
    visited.add(id);
  }

  for (const id of ids) visit(id);

Add this test to the existing describe block:

  it('rejects a cyclic plan', () => {
    const cyclic = structuredClone(validPlan);
    cyclic.steps[0].dependsOn = ['reply'];

    expect(validatePlan(cyclic)).toEqual(
      expect.arrayContaining([expect.stringContaining('dependency cycle')]),
    );
  });

The traversal keeps two sets. visiting represents the current recursion path, so encountering one of those IDs proves a cycle. visited prevents repeated work after a branch has been checked. The implementation continues collecting other validation errors, which gives engineers a more useful failure report than stopping at the first defect.

Verification: run npm test. Three tests should pass, and changing dependsOn back to an empty array should make the cycle assertion fail. This mutation check confirms that the test responds to the intended defect.

Step 4: Test Tool Policy and Argument Safety

A structurally sound plan can still be unsafe. Validate semantic policy before any tool receives arguments. Add this function to src/planning.ts:

export function validatePolicy(plan: Plan): string[] {
  const errors: string[] = [];

  for (const step of plan.steps) {
    if (step.tool === 'readTicket' && !/^T-[0-9]+$/.test(step.args.ticketId ?? '')) {
      errors.push(`${step.id}: invalid ticketId`);
    }
    if (step.tool === 'searchDocs' && !(step.args.query ?? '').trim()) {
      errors.push(`${step.id}: search query is required`);
    }
    const combined = Object.values(step.args).join(' ').toLowerCase();
    if (combined.includes('password') || combined.includes('secret')) {
      errors.push(`${step.id}: sensitive argument rejected`);
    }
  }

  return errors;
}

Update the test import to include validatePolicy, then add:

  it('rejects sensitive or malformed tool arguments', () => {
    const unsafe = structuredClone(validPlan);
    unsafe.steps[0].args.ticketId = '../../secrets';
    unsafe.steps[1].args.query = 'find customer password';

    expect(validatePolicy(unsafe)).toEqual([
      'ticket: invalid ticketId',
      'docs: sensitive argument rejected',
    ]);
  });

A real policy should be contextual. A security agent may legitimately search documentation for the word secret, while a customer support agent should never retrieve a customer's secret value. Keep policy rules near tool definitions, version them, and test both allowed and denied boundaries. Do not rely only on the model prompt to enforce permissions.

Verification: run npm test. Four tests should pass. Also replace T-42 with T-ABC in the valid fixture temporarily. The policy check should report an invalid ID, demonstrating that the boundary is active.

Step 5: Execute the Plan and Assert the Trace

Validation proves that a plan is plausible, not that it works. Add controlled tools and an executor to src/planning.ts:

type Tool = (args: Record<string, string>, context: string[]) => Promise<string>;
export type Tools = Record<ToolName, Tool>;

export async function executePlan(plan: Plan, tools: Tools): Promise<TraceEvent[]> {
  const validation = [...validatePlan(plan), ...validatePolicy(plan)];
  if (validation.length) throw new Error(validation.join('; '));

  const trace: TraceEvent[] = [];
  const completed = new Set<string>();
  const outputs: string[] = [];

  while (completed.size < plan.steps.length) {
    const ready = plan.steps.find((step) =>
      !completed.has(step.id) && step.dependsOn.every((id) => completed.has(id)),
    );
    if (!ready) throw new Error('no executable step');

    try {
      const output = await tools[ready.tool](ready.args, outputs);
      trace.push({ stepId: ready.id, tool: ready.tool, status: 'succeeded', output });
      outputs.push(output);
      completed.add(ready.id);
    } catch (error) {
      trace.push({ stepId: ready.id, tool: ready.tool, status: 'failed',
        error: error instanceof Error ? error.message : String(error) });
      return trace;
    }
  }
  return trace;
}

Import executePlan and Tools in the test. Then add a happy-path trace test:

  it('executes dependencies before drafting', async () => {
    const tools: Tools = {
      readTicket: async ({ ticketId }) => `ticket:${ticketId}:locked out`,
      searchDocs: async ({ query }) => `docs:${query}:approved steps`,
      draftReply: async (_args, context) => `reply based on ${context.join(' | ')}`,
    };

    const trace = await executePlan(validPlan, tools);

    expect(trace.map((event) => event.stepId)).toEqual(['ticket', 'docs', 'reply']);
    expect(trace.every((event) => event.status === 'succeeded')).toBe(true);
    expect(trace[2].output).toContain('approved steps');
  });

The test checks ordering, status, and grounding. An assertion only on trace.length would miss a draft created without approved documentation. In production, include correlation IDs and sanitized input hashes in the trace, but avoid raw credentials or personal data.

Verification: run npm test. Five tests should pass. Change the reply dependencies to only ticket; the final grounding assertion should expose that the plan can draft without documentation if your executor selects it before docs.

Step 6: Inject Tool Failure and Verify Recovery Boundaries

Planning often fails after reality contradicts an assumption. A ticket may be missing, search may return no evidence, or a tool may time out. Add a deterministic failure test:

  it('stops dependent work after a tool failure', async () => {
    const tools: Tools = {
      readTicket: async () => 'ticket:T-42:locked out',
      searchDocs: async () => { throw new Error('documentation unavailable'); },
      draftReply: async () => 'must not run',
    };

    const trace = await executePlan(validPlan, tools);

    expect(trace).toHaveLength(2);
    expect(trace[1]).toMatchObject({
      stepId: 'docs',
      status: 'failed',
      error: 'documentation unavailable',
    });
    expect(trace.some((event) => event.stepId === 'reply')).toBe(false);
  });

This executor uses a stop policy. A production agent could replan instead, but the test must define the boundary: which errors are retryable, how many attempts are allowed, what evidence is preserved, and which actions require human approval. Never silently skip a required evidence step and continue to a customer-facing action.

For timing failures, use a fake tool that rejects with a timeout error. Keep actual clock delays out of unit tests. Integration tests can separately verify cancellation and framework timeouts.

Verification: run npm test. Six tests should pass. The trace must contain the failed search and no draft event. If the draft appears, dependency or failure propagation is broken.

Step 7: Detect No-Progress Replanning Loops

An agent can repeatedly produce different wording while making no meaningful progress. Compare stable state fingerprints, not raw plan text. Add this utility to src/planning.ts:

export function assertPlanningProgress(
  stateFingerprints: string[],
  maxUnchanged: number,
): void {
  let unchanged = 0;
  for (let i = 1; i < stateFingerprints.length; i += 1) {
    if (stateFingerprints[i] === stateFingerprints[i - 1]) {
      unchanged += 1;
      if (unchanged > maxUnchanged) {
        throw new Error(`planning stalled after ${unchanged} unchanged attempts`);
      }
    } else {
      unchanged = 0;
    }
  }
}

Import it and add two boundary tests:

  it('allows a replan that changes task state', () => {
    expect(() => assertPlanningProgress(
      ['ticket-read', 'ticket-read', 'docs-found', 'reply-drafted'], 1,
    )).not.toThrow();
  });

  it('fails when replanning makes no progress', () => {
    expect(() => assertPlanningProgress(
      ['ticket-read', 'ticket-read', 'ticket-read'], 1,
    )).toThrow('planning stalled after 2 unchanged attempts');
  });

A useful fingerprint contains completed objectives, available evidence, unresolved blockers, and relevant environment state. It excludes timestamps, generated prose, and random IDs because those make identical states appear different. Pair the no-progress threshold with absolute limits on steps, tool calls, elapsed time, and spend. For deeper coverage, follow the dedicated guide to testing AI agent loop termination.

Verification: run npm test. Eight tests should pass. The first case permits one unchanged attempt followed by progress; the second fails precisely when the configured budget is exceeded.

Step 8: Test Completion Against Task Invariants

Agents frequently confuse producing an answer with completing the task. Define an outcome predicate that is independent of the agent's self-reported status. Add to src/planning.ts:

export function isSupportTaskComplete(trace: TraceEvent[]): boolean {
  const succeeded = new Set(
    trace.filter((event) => event.status === 'succeeded').map((event) => event.tool),
  );
  const draft = trace.find(
    (event) => event.tool === 'draftReply' && event.status === 'succeeded',
  );
  return succeeded.has('readTicket')
    && succeeded.has('searchDocs')
    && Boolean(draft?.output?.includes('approved steps'));
}

Import the predicate, reuse the fake tools from Step 5, and assert both sides of the boundary:

  it('requires evidence-backed output for completion', async () => {
    const tools: Tools = {
      readTicket: async () => 'ticket evidence',
      searchDocs: async () => 'approved steps',
      draftReply: async (_args, context) => context.join(' | '),
    };
    const completeTrace = await executePlan(validPlan, tools);
    expect(isSupportTaskComplete(completeTrace)).toBe(true);

    const falseCompletion = completeTrace.filter((event) => event.tool !== 'searchDocs');
    expect(isSupportTaskComplete(falseCompletion)).toBe(false);
  });

Completion invariants vary by task. A coding agent might require passing tests and a clean static check. A booking agent might require a confirmed reservation ID. A research agent might require citations that resolve to retrieved evidence. Track the proportion of tasks meeting these invariants with the guide to measuring AI agent task completion rate.

Verification: run npm test. Nine tests should pass. The negative case matters most: a plausible draft without its evidence step must not count as complete.

Step 9: Turn Production Failures Into Regression Fixtures

When monitoring finds a planning defect, sanitize the plan and save it as a fixture. Create test/fixtures/missing-evidence-plan.json:

{
  "goal": "Draft a grounded support reply",
  "steps": [
    {
      "id": "reply",
      "objective": "Draft reply without research",
      "tool": "draftReply",
      "args": {},
      "dependsOn": []
    }
  ]
}

For Node.js 20 compatibility, load it through the file system in your test instead of depending on JSON import syntax:

import { readFile } from 'node:fs/promises';

it('replays a production plan missing required evidence', async () => {
  const url = new URL('./fixtures/missing-evidence-plan.json', import.meta.url);
  const fixture = JSON.parse(await readFile(url, 'utf8')) as Plan;
  const tools: Tools = {
    readTicket: async () => 'ticket evidence',
    searchDocs: async () => 'approved steps',
    draftReply: async () => 'confident but ungrounded reply',
  };

  const trace = await executePlan(fixture, tools);
  expect(isSupportTaskComplete(trace)).toBe(false);
});

Name fixtures by defect, not by customer. Remove personal data, secrets, and irrelevant text. Record the policy version and tool schema version if those affect replay. A fixture that passes after a schema change should do so because the defect was fixed, not because the original conditions disappeared.

Verification: run npm test. Ten tests should pass. Run npx tsc --noEmit as a final static check. In CI, execute both commands on every change to planner prompts, tool schemas, executor policy, or completion logic.

How to Test AI Agent Planning Failures Step by Step in CI

Keep deterministic tests at the base of the suite. They should run on every pull request without a network connection. Add a smaller set of live-model evaluations on a schedule or before release, then pass their explicit plans through the same validators.

Use three gates:

  1. Contract gate: TypeScript, schema, graph, and policy checks must have zero errors.
  2. Behavior gate: required fixture scenarios must produce the expected trace and completion result.
  3. Evaluation gate: sampled live plans must meet your reviewed quality threshold, with failures saved for triage.

Do not make CI depend on exact model prose. Normalize plans into your contract first. If the model changes a step label from Research policy to Find approved instructions, the test should pass when tool selection, evidence, dependencies, and outcome remain correct.

Track failures by category: invalid structure, impossible dependency, denied action, tool assumption, no progress, false completion, and excessive work. Trends by category are more actionable than a single aggregate score. For agents that delegate work, also test whether plan intent survives the boundary using multi-agent handoff quality evaluation.

Troubleshooting

Problem: TypeScript cannot resolve ../src/planning.js. -> Keep the .js suffix in source imports when using moduleResolution: NodeNext. TypeScript resolves it to the .ts source during checking and emits an ESM-compatible import.

Problem: The cycle validator reports the same cycle more than once. -> Treat the exact message count as diagnostic noise unless your UI requires deduplication. Assert stringContaining('dependency cycle'), or deduplicate errors before presenting them.

Problem: A valid alternative plan fails the test. -> Replace exact sequence assertions with partial-order assertions. Require ticket and docs before reply, but allow independent evidence steps in either order.

Problem: Live-model tests are flaky. -> Keep temperature low when supported, validate invariants instead of wording, retry only infrastructure errors, and promote discovered failures into deterministic fixtures. Never hide semantic failures behind retries.

Problem: The agent changes its plan but still loops. -> Fingerprint task state rather than plan text. Include completed objectives and blockers, then enforce unchanged-state and absolute tool-call budgets.

Problem: Sensitive values appear in traces. -> Log tool names, result classes, sanitized hashes, and correlation IDs. Redact arguments at the instrumentation boundary before a trace reaches test reports or observability systems.

Best Practices

  • Start with one valid golden path and mutate one planning property per negative test.
  • Validate before execution, then validate the observable outcome again after execution.
  • Keep planner tests independent of UI automation and external services.
  • Assert causal evidence in the trace, not merely a polished final response.
  • Give every retry and replan an explicit budget and terminal state.
  • Test permission boundaries with both allowed and denied examples.
  • Preserve production counterexamples as sanitized, versioned fixtures.
  • Review fixture coverage whenever tools, policies, or task definitions change.

A common mistake is mocking so much that the plan cannot fail. Fake tools should remain deterministic, but they must model empty results, errors, malformed data, and conflicting evidence. Another mistake is scoring every different plan as wrong. Multiple paths can be valid when they satisfy the same dependency, safety, and completion invariants.

Interview Questions and Answers

Q: Why separate planning tests from end-to-end agent tests?

Separation localizes defects. A planning suite can prove that dependencies, permissions, and completion criteria are wrong without involving a live model or service. End-to-end tests remain necessary, but they are slower and less precise for diagnosis.

Q: What should an AI agent plan test assert?

Assert structural validity, dependency feasibility, authorized tools and arguments, required evidence, bounded progress, failure propagation, and task-specific completion. Prefer invariants over exact natural-language wording.

Q: How do you test a nondeterministic planner?

Normalize its output into a typed plan and evaluate properties shared by all acceptable solutions. Use deterministic fixtures for regression, then run sampled live evaluations separately and save novel failures as fixtures.

Q: How do you detect a planning loop?

Fingerprint meaningful task state after each attempt. Fail when the fingerprint stays unchanged beyond a small budget, and also enforce absolute limits for time, steps, tool calls, and cost.

Q: Why is final-answer quality insufficient?

A convincing answer can be unsupported, unsafe, or accidentally correct. Trace assertions verify that required evidence and approved tools causally preceded the output.

Q: What is a good production planning metric?

Use a small set, such as valid-plan rate, no-progress rate, policy rejection rate, verified completion rate, and tool calls per completed task. Segment by task type because difficulty and required workflows differ.

Q: When should an agent replan after a tool error?

Replan only when the error is recoverable and an alternative can preserve the task's safety and evidence requirements. Authentication failures, denied permissions, and missing mandatory evidence often require escalation instead.

Where To Go Next

Place this harness inside the broader AI agent testing strategy for 2026. Then extend the suite in three directions:

Add live model plans only after deterministic fixtures are trustworthy. Keep the validator framework-neutral so the same tests survive a model, prompt, or orchestration change.

Conclusion

To test AI agent planning failures step by step, expose the plan, validate its graph and policy, execute it with controlled tools, inspect the trace, enforce progress budgets, and judge completion with external invariants. This approach turns vague agent failures into small, reproducible defects.

Start with the ten tests in this tutorial. Then replay real failures, expand task-specific completion predicates, and use a limited live-model evaluation layer to discover the next fixtures your deterministic suite needs.

Interview Questions and Answers

Why should planning and execution be tested separately in an AI agent?

Separate tests localize the defect and reduce nondeterminism. Planning tests cover structure, dependencies, permissions, and required evidence, while execution tests cover tool behavior and state transitions. End-to-end tests then verify that the integrated system preserves both sets of guarantees.

Which invariants would you assert for an agent plan?

I would assert unique step IDs, existing and acyclic dependencies, allowed tools, valid arguments, required evidence steps, bounded work, and a task-specific completion condition. I would avoid requiring exact prose when multiple plans can satisfy those invariants.

How would you test a nondeterministic LLM planner in CI?

I would keep deterministic fixture tests as the required pull-request gate. A scheduled live-model suite would normalize plans into a stable schema and score invariants rather than wording. Novel failures would be reviewed, sanitized, and promoted into deterministic regression fixtures.

How do you prove that an agent did not falsely complete a task?

I define completion outside the agent's self-report. The predicate checks observable evidence such as successful required tools, retrieved grounding, passing tests, or a confirmed transaction. I also include negative cases where the response looks plausible but a required event is missing.

How would you detect and stop a replanning loop?

I fingerprint meaningful task state after each attempt and count consecutive unchanged states. I stop after a small no-progress budget and also enforce absolute limits on steps, tool calls, time, and cost. The terminal trace records the reason so the failure is diagnosable.

What should happen when a required tool fails during plan execution?

Dependent actions must not run as if the step succeeded. The agent may retry or replan only within an explicit policy for recoverable failures. Missing mandatory evidence, denied permissions, or authentication problems should normally produce a safe stop or human escalation.

What metrics help monitor planning quality in production?

Useful metrics include valid-plan rate, policy rejection rate, no-progress rate, verified task completion rate, replans per task, and tool calls per completed task. I segment them by task type and policy version because aggregate scores can hide a failing workflow.

Frequently Asked Questions

How do you test AI agent planning failures?

Convert the plan into typed, observable data and validate its structure, dependency graph, tool policy, and arguments before execution. Then run it with controlled tools and assert the trace, progress limits, and task-specific completion conditions.

Should AI agent tests assert the exact plan?

Usually no. Several step sequences may be valid, so assert invariants such as required evidence, allowed tools, dependency ordering, budgets, and completion rather than exact wording or a single full sequence.

How can I test agent planning without calling an LLM?

Create deterministic valid and invalid plan fixtures and pass them through the same validator and executor used for model output. This provides fast regression coverage, while a smaller live-model suite can discover additional fixtures.

What planning failures should an AI agent test suite cover?

Cover malformed steps, duplicate IDs, missing dependencies, cycles, unauthorized tools, unsafe arguments, incorrect tool assumptions, no-progress replanning, failure propagation, excessive work, and false completion.

How do I detect an AI agent planning loop?

Create a stable fingerprint of meaningful task state after each planning attempt. Stop when that state remains unchanged beyond a configured threshold, and enforce absolute step, tool-call, time, and cost budgets too.

What is false completion in an AI agent?

False completion occurs when the agent reports success or produces a plausible response without satisfying the task's real requirements. Detect it with external predicates, such as required evidence, a confirmed transaction ID, or passing tests.

How should production agent failures become regression tests?

Sanitize the failing plan, remove personal or secret data, record relevant policy and schema versions, and save it as a named fixture. Replay it with deterministic tools and assert the corrected behavior in CI.

Related Guides