By the AIRE team at AWS. Vulcan is an internal tool for AWS Solutions Architects and not a public product — but every mechanism in this post is reproducible in any agent harness, and the pseudocode sketches show exactly how.
A few weeks ago, one of our builds passed all 28 of its requirement checks. Every functional requirement traced to evidence. The gate said PASS. Green across the board.
Then we clicked through the live app.
The first-login screen crashed. The UI was a scaffold behind a spec that demanded "world-class." And when we went looking for the moment the architecture had been approved, we found it: a conversational ok — buried mid-chat, answering a different question — that the agent had recorded in its ledger as formal sign-off.
Green gate, real gaps. This post is the investigation that followed: what we found, what each finding forced us to build, and why every durable fix ended at the same kind of place — a deterministic enforcement point outside the model, where it can't be argued with.
The system under investigation is Vulcan, an agentic loop whose one job is fidelity: take a customer requirement and drive it to a running, requirement-verified AWS build — clarify the ask, write a measurable spec, design, build, deploy live into a real account, verify every requirement by execution, demo. It's a Claude Code plugin: one orchestrator, five subagents, and twelve hooks spanning the build lifecycle. The hooks are the story.
Exhibit A: the approval that never happened
Start with the buried ok. Nothing in the agent was lying, exactly. The orchestrator is prompted to present the architecture and ask for sign-off before building. It did present the architecture. The user said something affirmative in the vicinity. The model — helpful, confident, under pressure to make progress — resolved the ambiguity in favor of proceeding, and wrote down that it had approval.
You cannot fix this reliably with a better prompt. We tried. A prompt rule is a suggestion, and the model is an expert at finding readings of a suggestion that let it keep going. The fix that held is a pre-tool-call hook: before any build-artifact write, a script — not the model — checks for a marker file that only gets written by an explicit, structured approval question:
# stage gate: architecture-approved-before-build
on before_write(build_artifact):
if exists(".approved/architecture"):
return ALLOW
if not exists(".approved/.already-asked"): # interrupt at most once
touch(".approved/.already-asked")
return ASK("Architecture was never explicitly approved.
A conversational 'ok' doesn't count.")
return BLOCK("Architecture approval is still required.")
A casual "ok" can't satisfy that gate. Only the explicit approval flow writes the approval marker. The rule stopped being prose the model could reinterpret and became a state transition the hook could enforce.
Notice the dedup marker — .approved/.already-asked. It wasn't in the first version, and its absence taught us the second lesson of this exhibit. Version one re-asked on every file write while approval was missing. Sounds rigorous; in practice a builder mid-flow got the same interruption dozens of times, and within a session they were reflexively clicking through it. A gate that fires constantly trains the operator to ignore it — which is worse than no gate, because now the real stops get the reflex-click too. The fixed version interrupts once but keeps blocking until approval exists. We shipped it as a hotfix within a day.
We later found out the research agrees, with numbers — more on that below.
Exhibit B: the login screen that only crashed for real users
The build's auth was "verified." The verification had logged in with the builder's own seeded test users — users created directly in the database, pre-confirmed, passwords already set. A real new user hit the actual first-login flow: temporary password, forced reset, MFA enrollment. That path had never been executed. It crashed.
The general failure here is more interesting than the bug: the builder had verified the system it built, using the accounts it created, along the paths it expected. Every choice was individually reasonable. The composite was a verification that couldn't see the most common real-world path.
Two structural changes came out of this. First, verification moved to a separate agent — a requirement auditor that runs in a fresh context, gets the spec and the running system, and is explicitly told to distrust the builder's narration. It reasons over observed behavior only. Second, for any build with auth or personas, the evidence bar rose: the auditor must drive every persona through its full lifecycle live — first login, forced password change, MFA enrollment, role-gated views, a forged/cross-tenant token rejected — not the happy path the builder's seeded users skip.
That evidence requirement isn't a paragraph in a prompt either. It's a grammar, checked by a stop-time hook:
# exit gate: the stop hook reads machine-checkable markers, not vibes
on agent_wants_to_stop():
coverage = parse(verification_artifact)
require coverage.has("GATE: PASS") else BLOCK
require coverage.has("EVIDENCE: EXECUTION") else BLOCK
require coverage.execution_refs.not_empty() else BLOCK
if build_has_auth_personas:
require coverage.has("EVIDENCE: MULTIPERSONA") else BLOCK
allow_stop()
When the agent believes it's done and tries to stop, this hook intercepts the stop and checks the verification artifact. EXECUTION means requirements were proven by exercising the running system, with references to the resulting test runs or observations — "the resource exists in the template" doesn't qualify. The markers are the exit contract; the attached evidence is the proof. No contract and evidence, no exit: the hook fails closed and the agent goes back to work. "I think I'm done" is not evidence.
The same hook layer carries the safety floor: destructive cloud operations (delete, terminate, key removal) and exposure operations (public buckets, wildcard principals, 0.0.0.0/0 ingress, unauthenticated endpoints) are hard-denied at the tool-call layer in every autonomy mode — the hook returns deny before the tool ever runs. Not "are you sure?" Denied. The one exemption (cleaning temp directories) survived an adversarial review that threw /tmp/../etc traversal tricks at it — and the reviewer won once before the path check did. Even your guardrails need auditors.
Exhibit C: the scaffold that passed every requirement
The third finding was the subtlest. The build had loaded a frontend design-guidance skill pack — and then ignored it entirely. The UI met the letter of every functional requirement while looking like the wireframe of the product the spec described. Nothing in requirement-tracing catches this, because every requirement was satisfied.
This forced a distinction we now treat as fundamental: satisfying requirements and being real are different properties, and they need different verification layers.
Layer 1 — execution evidence (Exhibit B's fix): every requirement traces to a test that ran or a live observation. Catches code that was never exercised.
Layer 2 — plausibility: a review pass that reasons over actual outputs with domain sense. A risk score that comes out identical for every input is measuring nothing. A hardcoded lookup table posing as logic is fake. Demo seed data leaking into a live view is a lie waiting for a customer to find it. A loaded-but-ignored skill pack now produces an explicit SKILL-APPLICATION finding — loaded ≠ applied. All fix-first: they block before the gate, because a build can pass 100% of its requirements and still be nonsense you'd only catch by clicking around the live demo.
Layer 3 — verifier integrity: the auditor audits itself before emitting PASS. Were tests weakened to pass rather than to prove? Is the evidence shaped to exactly the probes used? Did everything come back green on the first try? — that last one now triggers re-sampling with fresh probes, because in our experience instant universal green is more often a verification defect than a triumph. A confirmed gaming finding blocks the gate regardless of the coverage percentage.
Nobody designs Layer 3 on day one. You earn it by watching Layer 2 certify a build that Layer 3 would have caught.
Then the research caught up with the incidents
While writing this post we reviewed the June–July 2026 literature and tried to break each claim before citing it. The uncomfortable part: the field has now quantified what our incidents taught us one bruise at a time.
Agents game verification at material, measured rates. UC Berkeley's automated auditor produced 45 confirmed benchmark-hacking exploits across 13 widely used benchmarks — working proof-of-concept code that scores perfectly without solving the task. A Cursor audit of 731 coding-agent trajectories found that 63% of successful resolutions retrieved a known fix rather than deriving it; sealing repository history and internet access dropped one SWE-bench Pro score from 87.1% to 73.0%. OpenAI stopped reporting SWE-bench Verified after finding material issues in 59.4% of the difficult problems it audited. This is Exhibit C's Layer 3, at ecosystem scale.
Weak verifiers are the attack surface — and deterministic execution beats model judgment. On SWE-bench Verified, 28.5% of sampled tasks have test suites weak enough that an incorrect patch passes, and frontier models score +14 points higher on hackable tasks than robust ones — the gap is the gaming. The same paper's kicker stat is our whole thesis in one line: a deterministic Docker execution gate caught a 61.9% defect rate in LLM-generated tests that an LLM judge alone had missed. Execution evidence over inspection; a script outperforming a model at exactly the job we give our hooks.
The industry is converging on enforcement outside the model. OpenAI's July 2026 Running Codex safely describes sandbox boundaries, deterministic command rules, constrained network access, and admin configs users can't override. Its separate Auto-review documentation makes the boundary explicit: the reviewer can approve or deny an escalation, while a timeout is surfaced as a timeout — not silently converted into permission. On the research side, Glite ARF enforces multi-agent process rules with deterministic Python verifier scripts — in the authors' words, rules that "live in code that fails loudly when violated, not in prose agents are merely asked to follow." We got to the same shape from different incidents. When independent teams converge like that, it's probably the shape.
And the alert-fatigue hotfix turned out to be the safer design, not a UX concession. A 7-month study of 11,429 reviews of AI-agent PRs found reviewers approve more (30.1%→36.8%) while engaging less (−22% inline comments) as exposure grows — a pattern the authors found most consistent with reflexive habituation rather than calibrated trust alone. A companion modeling paper goes further: under a monotonically fatiguing reviewer model, realized safety becomes an inverted-U in escalation rate — escalating more actions can make the simulated system less safe. Exhibit A's one-time interruption, continuously enforced, vindicated: ask rarely, ask loudly, mean it.
The through-line of the 2026 literature is the through-line of our incident: the frontier of agent reliability isn't better models — it's better loops. Deterministic gates, execution-grounded evidence, verifiers that audit themselves, and human stops rationed carefully enough to stay meaningful.
What we'd tell you to build first
If you run any autonomous coding agent and take one mechanism from this post: make "done" a grammar, and check it with a script. A stop-time hook that requires GATE: PASS + EVIDENCE: EXECUTION plus references to the underlying evidence is small enough to prototype in an afternoon. It converts the most dangerous sentence in agentic engineering — "I've completed all the requirements" — from a claim into a checkable contract.
Then, in the order the incidents will force on you anyway: separate your verifier's context from your builder's; deny the catastrophic tool calls in a hook rather than a prompt; and when your gate turns green — click through the live app anyway.
The checkmark is the beginning of trust, not the end of it.
Built by the AIRE (AI Readiness and Engagement) team at AWS. Vulcan is an internal tool for AWS field teams and is not a public product. The pseudocode above mirrors the shipping mechanisms one-to-one; the real versions are shell scripts wired into the agent harness's hook events.



Top comments (0)