/** * stringifiedArrayGuard — diagnostic schema helper. * * Problem: * Some callers (observed in the repo-dataset marketing swarm, 2026-04-17) * serialize nested array params as JSON strings before the MCP transport * sees them. `source_paths: ["file.md"]` arrives at the server as * `"[\"file.md\"]"` — a string. Strict `z.array(z.string())` then fails * with a generic "expected array, received string", which hides the real * cause (upstream stringification) behind a schema error the caller can't * act on. * * Decision (user-directed, 2026-04-17): * Keep the public contract strict — do NOT silently coerce. Instead, when * a string arrives where an array is required, emit a diagnostic error * that explicitly names the upstream-stringification case and tells the * caller to fix their tool-call path. This keeps the product honest while * giving the offending caller a fast signal. If later evidence shows * multiple uncontrolled callers, a compatibility-coercion path can be * added behind an explicit, warning-counted flag — but not yet. * * Parent doc: memory/ollama-intern-output-quality-report-2026-04-17.md * Runbook: memory/ollama-intern-phase-a-runbook-2026-04-17.md */ import { z } from "zod"; export interface StrictStringArrayOpts { min?: number; max?: number; minItemLen?: number; fieldName?: string; } /** * Detail payload for an operator-facing structured event emitted when * the strict-string-array schema rejects upstream-stringified input. * * Stage B audited this guardrail as stderr-only — the diagnostic landed * in the MCP host's stderr capture but never in the structured NDJSON * log, so an operator triaging from log_tail couldn't see WHICH caller * had a broken serializer. This helper gives callers (or a thin wrapper * — see below) a path to emit the same signal as a structured event. * * Phase 7 / FT-001: `op` + `rule` stamped so jq filters uniformly with * `jq 'select(.op=="guardrail" and * .detail.rule=="stringified_array_guard")'`. The NDJSON logger * auto-merges `run_id` from the active AsyncLocalStorage * `CorrelationContext` at write time * (see observability.withCorrelation). * * The pre-existing stderr emit is preserved for back-compat with any * operator who already greps stderr; the new event is additive. */ export interface StringifiedArrayEventDetail { /** Closed-enum op tag from observability.CorrelationOp. Always 'guardrail' here. */ op: "guardrail"; /** Stable rule identifier — greppable. */ rule: "stringified_array_guard"; /** The schema field that was misformed (caller-supplied). */ field: string; /** * True when the offending value's contents look like a JSON-encoded * array (starts with [ and ends with ]). Lets an operator filter for * the most common cause (caller serializer JSON-encoding nested * params) versus a caller passing a single value where an array was * expected. */ looks_like_json_array: boolean; /** * Truncated preview of the offending value (first 80 chars). Enough * to recognize the shape; not enough to leak large inputs into the * log. */ value_preview: string; } /** * Build the structured-event detail for an operator log entry when the * strict-string-array guard rejected a stringified value. Callers with * access to the offending input and field name pass them in; the helper * does not have a logger of its own (Stage B kept it stderr-only and * the schema layer can't reach into the request handler's logger). * * Wrappers in src/tools/* that catch the schema error can call this * with the original `value` and `fieldName` to wire the structured * event alongside the existing stderr line. */ export declare function buildStringifiedArrayEvent(args: { value: unknown; fieldName?: string; }): StringifiedArrayEventDetail; /** * Strict array schema with diagnostic for upstream-stringified input. * * Behavior: * - array matching min/max/minItemLen: pass * - string (any shape): fail with specific diagnostic naming the * stringification case when the string looks like a JSON array, * or with a "wrap in array" hint otherwise * - anything else: fail with zod's normal messages * * Output type is `string[]`. Chain `.optional()` for optional fields. */ export declare function strictStringArray(opts?: StrictStringArrayOpts): z.ZodType; //# sourceMappingURL=stringifiedArrayGuard.d.ts.map