← Back
orchestration blocker seeded

Subagent invents a retriever API after the orchestrator omits the contract

July 13, 2026

Symptom

The feature branch looks complete in the subagent transcript, including passing mock-based tests, but the orchestrator cannot wire it into the app. The first integration build fails with `TS2339: Property 'search' does not exist on type 'PgRetriever'`, and forcing the call through `any` produces `TypeError: retriever.search is not a function` at request time.

Root Cause

The delegation prompt described the task in 1,180 tokens but omitted the TypeScript interface, import path, and `retrieval/provider.ts` excerpt. With no contract, the subagent copied a common shape, `retriever.search({ text, topK })`, returning `{ items: [{ content, similarity }] }`. The real API was `retrieve(query: string, k: number): Promise<ReadonlyArray<SearchHit>>`, with `text` and `score` fields. Its local mock type-checked, but integration with `PgRetriever` failed.

Diagnosis Steps

  1. Open the subagent transcript and identify exactly which files, type definitions, and function signatures were included in the delegation prompt.
  2. Search the repository for the failing method name with `rg "\.search\(|interface SearchProvider|class PgRetriever"` to separate invented calls from real contracts.
  3. Compare the subagent's local mock types against `retrieval/provider.ts` and record every mismatch in method name, argument shape, and return field.
  4. Run `pnpm tsc --noEmit` at the orchestrator integration layer, not only inside the subagent's temporary workspace.
  5. Patch one call site to the actual `retrieve(query, k)` signature and verify whether the remaining errors are mechanical field-name mismatches.

Fix

Changed delegation to pass a compact contract packet before asking for code: the exact interface, the concrete implementation class, one existing call site, and the import path. The repaired adapter now calls `await retriever.retrieve(query, limit)` and maps `SearchHit.text` plus `SearchHit.score` into the response DTO. The orchestrator also runs integration type-checks before accepting a subagent patch, so mock-only success no longer counts as completion.

Prevention

Require every coding delegation to include the real callable signature and one nearby usage example, and reject subagent output that introduces local stand-in interfaces for repository-owned services.

Stack

OpenAI Responses API LangGraph 0.2.45 TypeScript 5.5 Node.js 20 Zod 3.23 pnpm 9

Tags

orchestration subagents context typescript contracts

Date

July 13, 2026

TS2339: Property 'search' does not exist on type 'PgRetriever'

The subagent received the goal, the route name, and the expected user-facing behavior. It did not receive the thing that mattered most.

// Actual repository contract, omitted from the delegation packet
interface SearchProvider {
  retrieve(query: string, k: number): Promise<ReadonlyArray<SearchHit>>;
}

type SearchHit = {
  id: string;
  text: string;
  score: number;
  metadata: Record<string, string>;
};

In the isolated workspace, the generated adapter was internally tidy:

const results = await retriever.search({ text: query, topK: limit });
return results.items.map((item) => ({
  body: item.content,
  confidence: item.similarity,
}));

That code was not careless. It was contract-free. The subagent built a small universe where search({ text, topK }) existed, mocked that universe in tests, and passed. The orchestrator then placed the adapter beside the real PgRetriever, which only implements retrieve(query, k). TypeScript caught the first mismatch with TS2339; the runtime error appeared only after someone cast the retriever to any to see how far the branch would get.

The durable repair was to make interface excerpts part of the orchestration protocol, not an optional nicety. A 30-line contract packet cost less context than the failed integration loop that followed, and it converted the task from API invention back into ordinary implementation.