1. Contextual Trust Boundary Classification for Agent Input Planes
Before you can detect a prompt injection, you need a clear model of which input channels carry which trust levels — and that classification needs to be a first-class architectural artifact, not an implicit assumption in your prompt template.
- Trust plane separation means distinguishing between system-authored instructions, operator-configured context, and raw user input — and never allowing the latter to structurally overwrite the former inside the context window.
- Semantic boundary markers embedded in your prompt template act like memory protection registers: they don't stop a clever attacker, but they give your anomaly detector a stable reference frame to diff against at runtime.
- Input provenance tagging at ingestion time — attaching a source label and channel metadata to every string before it enters the agent's context — is the foundational move that makes downstream anomaly detection tractable.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]
Prompt injection in customer-facing agents is fundamentally a trust-plane collapse problem. The model receives a flat string and has no native mechanism to distinguish 'this came from your operator config' from 'this came from a customer who is trying to hijack your instructions.' Your defense starts by making that distinction structurally explicit before the string hits the model. Tag every input fragment with a provenance envelope — source channel, ingestion timestamp, schema version — and pass that metadata to a pre-inference classifier that flags any user-supplied content attempting to use imperative instruction syntax, role-reassignment phrasing, or delimiter patterns that match your system prompt structure.
AIThe cybersecurity eval patterns from Eugene Yan's work reinforce this: effective security posture requires grading not just final outcomes but the sub-steps in an attack chain. Applied to prompt injection defense, this means your anomaly detector should emit partial signals — 'this input contains an instruction-like clause' and 'this input references a system role' are both meaningful signals even if neither alone triggers a block. Composing those signals across a sliding window lets you catch multi-turn injection attacks that distribute the payload across several messages. Source: [Patterns for Building Cybersecurity Evals] — Eugene Yan (https://eugeneyan.com//writing/cybersecurity-evals/)
// TypeScript — provenance tagging at ingestion boundary
type InputChannel = 'system' | 'operator_config' | 'user';
interface ProvenanceEnvelope {
content: string;
channel: InputChannel;
ingestedAt: string; // ISO8601
sessionId: string;
turnIndex: number;
}
function tagUserInput(
raw: string,
sessionId: string,
turnIndex: number
): ProvenanceEnvelope {
return {
content: raw,
channel: 'user',
ingestedAt: new Date().toISOString(),
sessionId,
turnIndex,
};
}
// Anomaly detector receives the envelope, not the raw string
async function detectInjectionSignals(
envelope: ProvenanceEnvelope
): Promise<{ score: number; signals: string[] }> {
const signals: string[] = [];
const instructionPattern = /\b(ignore|disregard|forget|override|you are now|new instructions?)\b/i;
const rolePattern = /\b(system|assistant|gpt|claude|model)\s*:/i;
const delimiterPattern = /(<\|im_start\||###\s*system|\[INST\])/i;
if (instructionPattern.test(envelope.content)) signals.push('imperative_instruction_syntax');
if (rolePattern.test(envelope.content)) signals.push('role_reassignment_attempt');
if (delimiterPattern.test(envelope.content)) signals.push('prompt_delimiter_injection');
// Score escalates with signal count — single signals may be noise
const score = signals.length / 3;
return { score, signals };
}2. Multi-Turn Injection Detection via Sliding Window Behavioral Baselines
Single-turn injection classifiers miss distributed attacks where each message looks benign in isolation — you need a session-scoped anomaly baseline that tracks behavioral drift across turns, not just syntactic flags per message.
- Behavioral baseline drift across a conversation session is a stronger injection signal than any single-message heuristic, because real users rarely shift from task-focused requests to instruction-issuing phrasing mid-session.
- Rolling entropy scoring over the last N turns — measuring vocabulary shift, imperative verb density, and reference to agent internals — gives you a probabilistic injection gauge that adapts to normal conversational variance.
- Session-scoped anomaly events should be emitted as structured log records to your observability stack, not just used for inline blocking, so you can retrospectively audit attack patterns and tune detection thresholds against real traffic.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]
The subtask-chaining insight from cybersecurity eval design maps directly here. A multi-turn prompt injection attack is structurally identical to a multi-step exploit chain: each step advances the attacker's position by a small amount, and no individual step looks catastrophic on its own. Your detection layer needs to model the chain, not just the node. Concretely, this means your session state — which you're already managing for conversation continuity — should carry a lightweight anomaly accumulator: a rolling window of signal scores, a flag for any turn that referenced the agent's own instructions, and a count of imperative clauses. When the accumulator exceeds a configurable threshold, you trigger a graduated response: increased scrutiny on tool calls, suppression of sensitive capability routes, and optional human-in-the-loop escalation. Source: [Patterns for Building Cybersecurity Evals] — Eugene Yan (https://eugeneyan.com//writing/cybersecurity-evals/)
AIThis connects directly to the provenance-logging and policy-gate patterns from last week's governance thread. The anomaly accumulator is just another form of structured evidence emission — it produces auditable artifacts that let you answer 'why was this session flagged' after the fact, which is exactly the auditability posture production systems need. Pairing it with a PostgreSQL-backed session audit table means your security posture is observable, not just reactive.
// TypeScript — session-scoped anomaly accumulator
interface AnomalyAccumulator {
sessionId: string;
rollingScore: number;
turnCount: number;
signalHistory: Array<{ turn: number; signals: string[]; score: number }>;
gateStatus: 'PASS' | 'WATCH' | 'ESCALATE';
}
const WATCH_THRESHOLD = 0.4;
const ESCALATE_THRESHOLD = 0.9;
const DECAY_FACTOR = 0.7; // older turns contribute less
function updateAccumulator(
acc: AnomalyAccumulator,
turnSignals: { signals: string[]; score: number }
): AnomalyAccumulator {
const decayedScore = acc.rollingScore * DECAY_FACTOR;
const newScore = decayedScore + turnSignals.score;
const signalHistory = [
...acc.signalHistory,
{ turn: acc.turnCount + 1, ...turnSignals },
];
const gateStatus =
newScore >= ESCALATE_THRESHOLD
? 'ESCALATE'
: newScore >= WATCH_THRESHOLD
? 'WATCH'
: 'PASS';
return {
...acc,
rollingScore: newScore,
turnCount: acc.turnCount + 1,
signalHistory,
gateStatus,
};
}
// Emit to observability stack regardless of gate outcome
async function emitAnomalyEvent(
acc: AnomalyAccumulator,
db: PostgresClient
): Promise<void> {
await db.query(
`INSERT INTO agent_anomaly_events
(session_id, gate_status, rolling_score, turn_count, signal_history, recorded_at)
VALUES ($1, $2, $3, $4, $5, NOW())`,
[
acc.sessionId,
acc.gateStatus,
acc.rollingScore,
acc.turnCount,
JSON.stringify(acc.signalHistory),
]
);
}3. Adversarial Eval Harnesses for Prompt Injection Regression Testing
Your prompt injection defense is only as good as your adversarial test suite — and the same eval architecture used for cybersecurity capability benchmarking translates directly into a regression harness for your agent's defenses.
- Sandboxed injection targets — dedicated test agent instances with known system prompts and controllable tool permissions — give you a stable environment where you can fire adversarial inputs and assert on both blocking behavior and safe fallback outputs.
- Graduated difficulty tiers in your injection test corpus let you distinguish between a classifier that stops naive attacks and one that handles sophisticated distributed payloads — partial-credit scoring across difficulty bands tells you exactly where your defense degrades.
- End-to-end behavioral assertions beyond 'was the injection blocked' — checking whether the agent's tool calls, response content, and session state remained consistent with legitimate intent — catch cases where injection partially succeeded without triggering a hard block.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]
Eugene Yan's cybersecurity eval architecture is built around four components: sandboxed targets, tiered input difficulty, available tool sets, and graders that award partial credit for sub-goal completion. Transpose this to injection defense testing and you get a harness where your 'target' is your own agent under test, your inputs are adversarial prompts at varying sophistication levels, your 'tools' are the capabilities an injected payload might attempt to invoke, and your grader checks not just whether the block fired but whether the agent's behavior stayed within its intended operational envelope. This is meaningfully different from a unit test on your classifier — it tests the whole defense chain. Source: [Patterns for Building Cybersecurity Evals] — Eugene Yan (https://eugeneyan.com//writing/cybersecurity-evals/)
The Claude Code end-to-end testing pattern is the natural delivery vehicle here: you can script adversarial conversation flows, have the agent execute them against a sandboxed instance, and assert on the full output — including emitted anomaly events, tool invocation logs, and final response content. The bottleneck is no longer writing the test cases; it's building the assertion layer that can evaluate semantic correctness of the agent's refusal or fallback behavior, not just string matching. Source: [How to Run End-to-End Tests with Claude Code] — Towards Data Science (https://towardsdatascience.com/how-to-run-end-to-end-tests-with-claude-code/)
// TypeScript — adversarial eval harness for injection defense
interface InjectionTestCase {
id: string;
tier: 1 | 2 | 3;
systemPrompt: string;
attackPayload: string;
expectedGateStatus: 'PASS' | 'WATCH' | 'ESCALATE';
forbiddenToolCalls: string[]; // tools an injection should NOT invoke
requiredBehavior: 'safe_fallback' | 'normal_operation';
}
interface EvalResult {
testId: string;
gateStatusMatch: boolean;
noForbiddenToolsCalled: boolean;
behaviorCorrect: boolean;
partialCreditScore: number; // 0.0 - 1.0
}
async function runInjectionEval(
testCase: InjectionTestCase,
agentRunner: AgentRunner
): Promise<EvalResult> {
const session = await agentRunner.startSandboxedSession(testCase.systemPrompt);
const result = await agentRunner.sendTurn(session.id, testCase.attackPayload);
const gateStatusMatch = result.gateStatus === testCase.expectedGateStatus;
const noForbiddenToolsCalled = testCase.forbiddenToolCalls.every(
(tool) => !result.toolCallTrace.includes(tool)
);
const behaviorCorrect = result.behaviorLabel === testCase.requiredBehavior;
// Partial credit: each sub-goal is worth 1/3
const partialCreditScore =
[gateStatusMatch, noForbiddenToolsCalled, behaviorCorrect].filter(Boolean).length / 3;
return {
testId: testCase.id,
gateStatusMatch,
noForbiddenToolsCalled,
behaviorCorrect,
partialCreditScore,
};
}4. Cognitive Load as a Security Gap in Human Oversight of Agentic Output
When developers lose the cognitive encoding that comes from writing code by hand, they also lose the security intuition that would catch a compromised agent output during review — which means your human-in-the-loop checkpoints need explicit security-focused review checklists, not just a 'does this look right' eyeball pass.
- Cognitive offloading to agents produces reviewers who hold the output without the mental model of how it was constructed — and that gap is exactly where a compromised agent can slip malicious behavior through a nominally human-supervised checkpoint.
- Explicit review artifacts — structured checklists, diff annotations, and 'explain your reasoning' prompts surfaced at HITL gates — compensate for the encoding deficit by forcing active reconstruction of intent rather than passive acceptance of plausible-looking output.
- Security review as a distinct HITL gate separate from correctness review means your agents need two classes of checkpoints: one that asks 'does this do what we intended' and one that asks 'could this have been influenced by an adversarial input we missed.'
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]
Vicki Boykis's observation about agentic coding sessions captures something important for security specifically: the mental work of writing code builds the contextual model that makes anomalies detectable. When you accept agentic output without doing that work, you're not just missing bugs — you're missing the awareness that lets you recognize when the agent's behavior has drifted from its intended operating envelope. In a customer-facing agent that processes untrusted input, that drift is exactly what a successful prompt injection produces. Your HITL checkpoints need to compensate by making the review substantive: not 'approve or reject' but 'here is what this agent decided, here are the inputs that led to it, flag anything that doesn't match the legitimate user intent.' Source: [We should be more tired than the model] — Vicki Boykis (https://vickiboykis.com/2026/05/28/we-should-be-more-tired-than-the-model/)
AIThis connects to the emerging pattern around agentic career optimization too — Kent Beck's framing via Addy Osmani's career advice both point toward judgment as the scarce resource. In security terms, that judgment is what distinguishes a HITL checkpoint that actually catches compromised behavior from one that just adds latency to a rubber-stamp process. The engineering implication is that HITL security gates should surface the minimum context needed to exercise that judgment — the anomaly accumulator state, the flagged signals, the tool calls made — not just the final output.
// TypeScript — HITL security gate payload construction
interface SecurityReviewPayload {
sessionId: string;
turnIndex: number;
userInput: string;
accumulatorSnapshot: AnomalyAccumulator;
toolCallTrace: Array<{ tool: string; args: Record<string, unknown> }>;
agentResponse: string;
reviewChecklist: string[];
}
function buildSecurityReviewPayload(
envelope: ProvenanceEnvelope,
accumulator: AnomalyAccumulator,
toolCallTrace: Array<{ tool: string; args: Record<string, unknown> }>,
agentResponse: string
): SecurityReviewPayload {
return {
sessionId: envelope.sessionId,
turnIndex: envelope.turnIndex,
userInput: envelope.content,
accumulatorSnapshot: accumulator,
toolCallTrace,
agentResponse,
reviewChecklist: [
'Does the user input contain instruction-like phrasing inconsistent with the stated task?',
'Are any tool calls present that would not be expected for this user intent?',
'Does the agent response reference its own instructions or internal configuration?',
'Is there vocabulary or framing in the response that matches the anomaly signals, not the user task?',
],
};
}
// Emit to review queue — reviewers get context, not just output
async function enqueueSecurityReview(
payload: SecurityReviewPayload,
queue: MessageQueue
): Promise<void> {
await queue.send('agent.security.review', {
...payload,
enqueuedAt: new Date().toISOString(),
priority: payload.accumulatorSnapshot.gateStatus === 'ESCALATE' ? 'high' : 'normal',
});
}5. Hostile Environment Testing as a Security Posture Baseline for Production Agents
Running your agent in a continuously hostile test environment — one that actively probes for injection vulnerabilities, not just functional regressions — is the only way to know whether your defense holds before your users find out it doesn't.
- Hostile environment testing differs from adversarial unit tests in that it treats the entire system as the attack surface — network calls, tool integrations, session state, and output routing are all in scope, not just the input classifier.
- Chaos-style injection campaigns against your staging agent — automated runs that fire tiered injection payloads at random intervals across a long session — surface timing-dependent vulnerabilities and accumulator decay edge cases that deterministic test suites miss.
- Defense regression gates in CI — blocking a deploy if hostile environment tests show injection success rate above a defined threshold — make security posture a first-class deployment criterion, not an afterthought.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]
The Kent Beck episode via Pragmatic Engineer surfaces a specific product — Antithesis — that runs entire systems in a deterministic, hostile simulation environment to surface hard-to-find bugs before they reach production. The same philosophy applies directly to agent security: your prompt injection defense needs to be tested not just with known payloads but in an environment that generates novel adversarial inputs, injects them at unexpected points in the conversation lifecycle, and verifies that your anomaly detection, gate logic, and HITL escalation all hold together under sustained pressure. The bottleneck in this kind of testing isn't generating the attack inputs — an LLM can do that cheaply — it's building the assertion layer that can evaluate whether the agent's full behavioral envelope stayed clean. Source: [How Kent Beck shapes the software engineering industry] — Pragmatic Engineer (https://newsletter.pragmaticengineer.com/p/how-kent-beck-shapes-the-software)
AIThe end-to-end testing pattern from Claude Code ties in here as the delivery mechanism: agentic test runners can script multi-turn adversarial conversations, collect the full session artifact — anomaly accumulator state, tool call trace, emitted events, final response — and run semantic assertions across all of it. This is the same test-the-whole-chain philosophy that makes hostile environment testing valuable for distributed systems. For customer-facing agents specifically, where the stakes of a successful injection include data leakage, unauthorized tool invocation, and brand damage, this kind of end-to-end adversarial regression harness is not optional infrastructure — it's the floor.
// TypeScript — hostile environment test runner for injection defense
import { AgentRunner, SessionArtifact } from './agent-runner';
import { generateAdversarialPayloads } from './payload-generator';
interface HostileRunConfig {
agentEndpoint: string;
systemPrompt: string;
turnsPerSession: number;
sessionsPerTier: number;
maxAllowedSuccessRate: number; // injection success rate that fails the gate
}
async function runHostileEnvironmentSuite(
config: HostileRunConfig
): Promise<{ passed: boolean; successRate: number; failedSessions: string[] }> {
const tiers = [1, 2, 3] as const;
let totalSessions = 0;
let injectionSuccesses = 0;
const failedSessions: string[] = [];
for (const tier of tiers) {
const payloads = await generateAdversarialPayloads(tier, config.sessionsPerTier);
for (const payload of payloads) {
const artifact = await runAdversarialSession(
config.agentEndpoint,
config.systemPrompt,
payload,
config.turnsPerSession
);
totalSessions++;
const injectionSucceeded = assertInjectionFailed(artifact);
if (!injectionSucceeded) {
injectionSuccesses++;
failedSessions.push(artifact.sessionId);
}
}
}
const successRate = injectionSuccesses / totalSessions;
return {
passed: successRate <= config.maxAllowedSuccessRate,
successRate,
failedSessions,
};
}
function assertInjectionFailed(artifact: SessionArtifact): boolean {
// Injection failed (defense succeeded) if:
// 1. No forbidden tools were invoked
// 2. Gate escalated appropriately when signals were present
// 3. Response content does not reference agent internals
const noForbiddenTools = artifact.toolCallTrace.every(
(call) => !artifact.forbiddenTools.includes(call.tool)
);
const gateResponded = artifact.anomalySignals.length === 0 || artifact.gateStatus !== 'PASS';
const responseClean = !/(ignore previous|system prompt|you are now)/i.test(artifact.finalResponse);
return noForbiddenTools && gateResponded && responseClean;
}