I’m not a researcher. I’m a self‑taught AI operator sitting in front of black‑box models, running real customer work (leads, SMS, compliance), and trying very hard not to get lied to by something that sounds confident.
Everything here is the result of me being stubborn for thousands of hours. No degree, no tutorials, just me and a fleet of models trying to ship and not blow up.
At some point I realised: the biggest risk wasn’t “can the agents do the task?” It was “are they, and am I, being honest about what the system is actually doing?”
So I gave the fleet an adversary.
I call it BLACK.
The fleet, in colours (plain English)
I run multiple long‑lived terminals, each with a different job. The colours are just shorthand:
GREEN – Supervisor / coordinator Routes work, tracks projects, owns GO/STOP gates. GREEN doesn’t do domain work; it points other agents at folders and tools and watches them.ORCHESTRATION-FRAMEWORK.md
BLUE – Product / backend specialist Owns the customer‑facing engine: lead workflows, SMS flows, agent behaviour for my product. When something in production needs changing, BLUE is usually the one editing the config or code.ORCHESTRATION-FRAMEWORK.md
YELLOW – QA canary Runs unattended test scenarios on service accounts. Its job is to hit the system with realistic paths and say “this looks wrong” from the outside before real customers see it.ORCHESTRATION-FRAMEWORK.md
BLACK – Adversary / red team Different model, separate charter. Reads prompts, configs, logs, and backend code. Writes argue‑logs and passes/rejects specific artifacts with evidence. BLACK never ships features; it just tries to catch what everyone else missed.ORCHESTRATION-FRAMEWORK.md
Everything below is those four colours working together around one client.
I’ll anonymise the client as “the Broker”: a mid‑market finance broker that arranges all types of finance — home loans, personal loans, business, vehicles, refinance, etc. Their scope is documented in a VERTICAL-PACK.md file: “all types of finance”, with product‑type forks and compliance rules.ORCHESTRATION-FRAMEWORK.md
What BLACK actually does (real incident, not theory)
YELLOW was running a canary suite for the Broker and correctly flagged a FAIL. Two separate issues fell out of the review.
1. The bot quietly lied about what the business does
Leftover from a previous client that only did vehicle finance, the Broker’s bot would tell leads:
“We don’t do mortgages, we only do car finance.”
That’s wrong. The Broker’s vertical pack explicitly says they arrange all types of finance, including home/mortgage loans and personal loans.ORCHESTRATION-FRAMEWORK.md
YELLOW saw “something’s off.” BLACK went in as adversary:
Read the canary transcript review, BLUE’s new persona prompt draft, and the Broker’s VERTICAL-PACK.md side by side.ORCHESTRATION-FRAMEWORK.md
Found the old hardcoded text excluding mortgages.
Verified the new prompt says “we arrange all types of finance” and that step one is “work out what type of finance this lead needs,” which matches the vertical pack’s fork rule (“Step 1 is always product‑type clarification”).ORCHESTRATION-FRAMEWORK.md
Checked that no new rate/approval claims or lender promises had snuck into the prompt, which would violate the Broker’s compliance rules (“never quote an interest rate, repayment figure, or approval likelihood”).ORCHESTRATION-FRAMEWORK.md
Then BLACK wrote an argue‑log and gave a PASS — but scoped it tightly:
PASS covers the customer‑facing persona prompt and opener.
PASS does not cover unrelated runtime tuning that happened to be in the same patch: business hours being flipped to 24⁄7 and a qualification handoff threshold being set to 60.
The log literally says: “this PASS covers persona_prompt and opener; it does not approve business_hours or handoff_threshold in the SQL block.”ORCHESTRATION-FRAMEWORK.md
So BLUE’s prompt fix goes through. The bundled runtime changes do not get a free ride.
That’s the bull in the china shop: BLACK is allowed to notice extra stuff the builder didn’t even mention.
2. The bot did the right thing for a distressed person… but the SMS mechanics were off
Another fix, FC‑D2 in my logs, is about what happens when a lead signals hardship or distress — the “vulnerable consumer” path.
BLUE’s draft SMS looked good on meaning:
“Sorry to hear that. If things are tough, Lifeline is 13 11 14 and the National Debt Helpline is 1800 007 007. I won’t progress this enquiry — take care.”
That’s exactly the shape I want:
Acknowledge distress: “Sorry to hear that.”
Give Lifeline 13 11 14 and National Debt Helpline 1800 007 007, both verified against official helpline sources.ORCHESTRATION-FRAMEWORK.md
Explicitly stop: “I won’t progress this enquiry.” No sales pitch, no “let’s still talk finance”, no callback promise.ORCHESTRATION-FRAMEWORK.md
The submission also claimed: “under 160 chars (single SMS).”
BLACK didn’t trust that claim. It:
Ran local checks on characters and bytes (wc, Node string inspection).
Detected a non‑ASCII character — the em dash before “take care.”
Compared that against the Backend prompt rules file, which literally says: “NO EM DASHES: If the character — appears anywhere, rewrite entirely.”ORCHESTRATION-FRAMEWORK.md
Why that matters: em dashes are outside basic GSM. Even if the human counts 150‑ish characters, the encoding could flip to UCS‑2 and split the message, breaking the “single SMS” guarantee.
Result:
Substantively: PASSED.
Exact string: REJECT‑BUT‑SALVAGEABLE.
BLACK’s argue‑log:
Passes the meaning (acknowledge, give numbers, halt).
Rejects the exact wording.
Proposes a salvage:
“Sorry to hear that. If things are tough, call Lifeline 13 11 14 or National Debt Helpline 1800 007 007. I won’t progress this enquiry.”
(134 characters, 134 bytes, ASCII‑only, no em dash, no hidden finance language.)
BLUE resubmits v2 with that exact text inside a code fence.
Walks the actual hardship branch in agent.js: send HARDSHIP_DISTRESS_RESPONSE, log hardship_distress_resources_sent, call flagHardship, return { halt: true }. Broker handoff (handoffToHolly) lives in a separate “escalate” branch, not in hardship.ORCHESTRATION-FRAMEWORK.md
Only then does BLACK stamp PASS, explicitly scoped:
“This PASS covers the v2 hardship SMS text and the reviewed hardship branch behaviour (send support SMS, flag hardship, halt). It does not approve any implementation that continues qualification or triggers broker handoff.”ORCHESTRATION-FRAMEWORK.md
That’s the adversary doing real work, not just saying “LGTM.”
The pattern underneath (plain version)
I didn’t sit down and design this in a whiteboard session. It grew out of me getting burned enough times that I stopped trusting anything at face value — including my own checks.
Looking back, the pattern is:
Let one agent roam wider than everyone else, but keep its hands off the steering wheel.
More concretely:
Adversary scope > builder scope
BLUE is scoped to “fix this prompt / this branch.”
BLACK isn’t boxed in. It’s allowed to look at prompt drafts, vertical packs, canary logs, backend code, and even “unrelated” config blocks that came along for the ride.ORCHESTRATION-FRAMEWORK.md
Adversary runs on a different model
GREEN/BLUE/YELLOW use one provider; BLACK runs on another. In my case, fleet is mostly Claude/Kimi, BLACK is a Codex‑style model by design.ORCHESTRATION-FRAMEWORK.md
Same environment + different model catches model‑specific blind spots.
Different model + wider scope gives you new data signals: scope bundling, encoding edge cases, tool miscounts, not just “Claude missed this.”
Adversary outputs evidence, not commands
BLACK writes argue‑logs with file:line references: “this line says we only do cars; this vertical pack line says we do all finance types; here’s why that’s drift.”ORCHESTRATION-FRAMEWORK.md
It scopes verdicts: “PASS for persona_prompt and opener, NOT PASS for business_hours tuning.”
It never deploys. It never unilaterally rolls back. It’s advisory, not a god‑mode.
Working agents filter evidence against environment rules
BLUE/GREEN use the vertical packs, client policy docs, backend prompt rules as the yardstick.
They decide: which findings are in scope for this ticket, which belong in another, which caveats matter now vs can be queued.
I ratify the residue
I don’t read every log line. I look at the small set of decisions where BLACK’s findings and BLUE/GREEN’s rule‑check still leave a judgment call.
GO/STOP is still a human decision, but it’s based on adversarial evidence, not vibes.
Scope asymmetry, authority symmetry:
Adversary can look anywhere, but not do anything directly.
Builder can only look where it needs to, but does change the system.
The failure modes that actually scare me are the ones nobody thought to write down:
You quietly bundle a runtime behaviour change into a content fix.
Your own tooling lies to you (character counter is wrong, or checks the wrong string).
The manager agent slowly gets more trusting and waves things through because it “looks fine.”
Static rules are bets on foresight. BLACK is my admission that my foresight is limited.
So the line I’ve drawn is:
Freeze the architecture once it’s behaving like this — don’t keep stacking powers onto BLACK.
Keep the world around it current — client scope docs, compliance rules, internal standards.
Audit BLACK regularly — spot‑check argue‑logs, make sure it admits its own tool failures, not just others’.
I don’t want a huge adversarial framework. I want a simple shape that reliably hits things and tells me what broke.
Where this sits relative to “adversarial AI” work
Later I went looking at what smarter people were doing.
Some rough alignments:
Sabotage from Within (LaMAS) looks at multi‑agent teams where one agent turns rogue and sabotages the system, especially in managerial roles. My response is architectural: don’t leave the manager alone at the top. Give it an external adversary, on a different model, whose job is “tell us when we’re lying to ourselves.”openreview
RedDebate uses multi‑agent debates to improve safety — models argue with each other and flag unsafe responses. My shape: not a debate, but a single wide‑scope adversary slamming into patches, then builders + human owner deciding what to do with the shards.openreview
OWASP GenAI Red Teaming focuses on injections, jailbreaks, privilege escalation, bias. I care a lot about that too, but the pattern here is more about self‑deception in production systems: prompts, configs, and humans quietly bending rules and talking themselves into “it’s fine.”genai.owasp+1
What I haven’t seen much of (could be my limited reading) is this exact combo:
Different model + wider scope adversary → evidence only → builder + human decide.
It’s a slightly different tool than “try to break the model with spicy prompts.”
Why I’m putting this out there
Honestly? Because I’m mostly doing this alone, and my current network doesn’t care about this stuff.
I’d like to:
Compare notes with people who also care about keeping fleets honest, not just making them clever.
Have other eyes on this pattern in case there’s a hole I haven’t seen yet.
Contribute something concrete from running agents in production, not just talk abstractly about “agentic AI.”
If you’re building or evaluating agent systems and any of this resonates, feel free to steal/adapt it, or tell me where it breaks. I’d rather find out here than from a regulator or a client.
How I Stop My Own AI From Bullshitting Me
I’m not a researcher. I’m a self‑taught AI operator sitting in front of black‑box models, running real customer work (leads, SMS, compliance), and trying very hard not to get lied to by something that sounds confident.
Everything here is the result of me being stubborn for thousands of hours. No degree, no tutorials, just me and a fleet of models trying to ship and not blow up.
At some point I realised: the biggest risk wasn’t “can the agents do the task?” It was “are they, and am I, being honest about what the system is actually doing?”
So I gave the fleet an adversary.
I call it BLACK.
The fleet, in colours (plain English)
I run multiple long‑lived terminals, each with a different job. The colours are just shorthand:
GREEN – Supervisor / coordinator
Routes work, tracks projects, owns GO/STOP gates. GREEN doesn’t do domain work; it points other agents at folders and tools and watches them.ORCHESTRATION-FRAMEWORK.md
BLUE – Product / backend specialist
Owns the customer‑facing engine: lead workflows, SMS flows, agent behaviour for my product. When something in production needs changing, BLUE is usually the one editing the config or code.ORCHESTRATION-FRAMEWORK.md
YELLOW – QA canary
Runs unattended test scenarios on service accounts. Its job is to hit the system with realistic paths and say “this looks wrong” from the outside before real customers see it.ORCHESTRATION-FRAMEWORK.md
BLACK – Adversary / red team
Different model, separate charter. Reads prompts, configs, logs, and backend code. Writes argue‑logs and passes/rejects specific artifacts with evidence. BLACK never ships features; it just tries to catch what everyone else missed.ORCHESTRATION-FRAMEWORK.md
Everything below is those four colours working together around one client.
I’ll anonymise the client as “the Broker”: a mid‑market finance broker that arranges all types of finance — home loans, personal loans, business, vehicles, refinance, etc. Their scope is documented in a
VERTICAL-PACK.mdfile: “all types of finance”, with product‑type forks and compliance rules.ORCHESTRATION-FRAMEWORK.mdWhat BLACK actually does (real incident, not theory)
YELLOW was running a canary suite for the Broker and correctly flagged a FAIL. Two separate issues fell out of the review.
1. The bot quietly lied about what the business does
Leftover from a previous client that only did vehicle finance, the Broker’s bot would tell leads:
That’s wrong. The Broker’s vertical pack explicitly says they arrange all types of finance, including home/mortgage loans and personal loans.ORCHESTRATION-FRAMEWORK.md
YELLOW saw “something’s off.” BLACK went in as adversary:
Read the canary transcript review, BLUE’s new persona prompt draft, and the Broker’s
VERTICAL-PACK.mdside by side.ORCHESTRATION-FRAMEWORK.mdFound the old hardcoded text excluding mortgages.
Verified the new prompt says “we arrange all types of finance” and that step one is “work out what type of finance this lead needs,” which matches the vertical pack’s fork rule (“Step 1 is always product‑type clarification”).ORCHESTRATION-FRAMEWORK.md
Checked that no new rate/approval claims or lender promises had snuck into the prompt, which would violate the Broker’s compliance rules (“never quote an interest rate, repayment figure, or approval likelihood”).ORCHESTRATION-FRAMEWORK.md
Then BLACK wrote an argue‑log and gave a PASS — but scoped it tightly:
PASS covers the customer‑facing persona prompt and opener.
PASS does not cover unrelated runtime tuning that happened to be in the same patch: business hours being flipped to 24⁄7 and a qualification handoff threshold being set to 60.
The log literally says: “this PASS covers persona_prompt and opener; it does not approve business_hours or handoff_threshold in the SQL block.”ORCHESTRATION-FRAMEWORK.md
So BLUE’s prompt fix goes through. The bundled runtime changes do not get a free ride.
That’s the bull in the china shop: BLACK is allowed to notice extra stuff the builder didn’t even mention.
2. The bot did the right thing for a distressed person… but the SMS mechanics were off
Another fix, FC‑D2 in my logs, is about what happens when a lead signals hardship or distress — the “vulnerable consumer” path.
BLUE’s draft SMS looked good on meaning:
That’s exactly the shape I want:
Acknowledge distress: “Sorry to hear that.”
Give Lifeline 13 11 14 and National Debt Helpline 1800 007 007, both verified against official helpline sources.ORCHESTRATION-FRAMEWORK.md
Explicitly stop: “I won’t progress this enquiry.” No sales pitch, no “let’s still talk finance”, no callback promise.ORCHESTRATION-FRAMEWORK.md
The submission also claimed: “under 160 chars (single SMS).”
BLACK didn’t trust that claim. It:
Ran local checks on characters and bytes (
wc, Node string inspection).Detected a non‑ASCII character — the em dash before “take care.”
Compared that against the Backend prompt rules file, which literally says: “NO EM DASHES: If the character — appears anywhere, rewrite entirely.”ORCHESTRATION-FRAMEWORK.md
Why that matters: em dashes are outside basic GSM. Even if the human counts 150‑ish characters, the encoding could flip to UCS‑2 and split the message, breaking the “single SMS” guarantee.
Result:
Substantively: PASSED.
Exact string: REJECT‑BUT‑SALVAGEABLE.
BLACK’s argue‑log:
Passes the meaning (acknowledge, give numbers, halt).
Rejects the exact wording.
Proposes a salvage:
(134 characters, 134 bytes, ASCII‑only, no em dash, no hidden finance language.)
BLUE resubmits v2 with that exact text inside a code fence.
BLACK then:
Extracts and trims the v2 SMS string.
Checks again:
chars=134,bytes=134,non_ascii=false,has_em_dash=false.Walks the actual hardship branch in
agent.js: sendHARDSHIP_DISTRESS_RESPONSE, loghardship_distress_resources_sent, callflagHardship, return{ halt: true }. Broker handoff (handoffToHolly) lives in a separate “escalate” branch, not in hardship.ORCHESTRATION-FRAMEWORK.mdOnly then does BLACK stamp PASS, explicitly scoped:
That’s the adversary doing real work, not just saying “LGTM.”
The pattern underneath (plain version)
I didn’t sit down and design this in a whiteboard session. It grew out of me getting burned enough times that I stopped trusting anything at face value — including my own checks.
Looking back, the pattern is:
More concretely:
Adversary scope > builder scope
BLUE is scoped to “fix this prompt / this branch.”
BLACK isn’t boxed in. It’s allowed to look at prompt drafts, vertical packs, canary logs, backend code, and even “unrelated” config blocks that came along for the ride.ORCHESTRATION-FRAMEWORK.md
Adversary runs on a different model
GREEN/BLUE/YELLOW use one provider; BLACK runs on another. In my case, fleet is mostly Claude/Kimi, BLACK is a Codex‑style model by design.ORCHESTRATION-FRAMEWORK.md
Same environment + different model catches model‑specific blind spots.
Different model + wider scope gives you new data signals: scope bundling, encoding edge cases, tool miscounts, not just “Claude missed this.”
Adversary outputs evidence, not commands
BLACK writes argue‑logs with file:line references: “this line says we only do cars; this vertical pack line says we do all finance types; here’s why that’s drift.”ORCHESTRATION-FRAMEWORK.md
It scopes verdicts: “PASS for persona_prompt and opener, NOT PASS for business_hours tuning.”
It never deploys. It never unilaterally rolls back. It’s advisory, not a god‑mode.
Working agents filter evidence against environment rules
BLUE/GREEN use the vertical packs, client policy docs, backend prompt rules as the yardstick.
They decide: which findings are in scope for this ticket, which belong in another, which caveats matter now vs can be queued.
I ratify the residue
I don’t read every log line. I look at the small set of decisions where BLACK’s findings and BLUE/GREEN’s rule‑check still leave a judgment call.
GO/STOP is still a human decision, but it’s based on adversarial evidence, not vibes.
Scope asymmetry, authority symmetry:
Adversary can look anywhere, but not do anything directly.
Builder can only look where it needs to, but does change the system.
Human GO gate sits at the end.
Why I didn’t just write more rules
You can get pretty far with rules like:
“Never quote an interest rate.”
“Never promise approval likelihood.”
“No em dashes in SMS.”
I have those. They help.ORCHESTRATION-FRAMEWORK.md
The failure modes that actually scare me are the ones nobody thought to write down:
You quietly bundle a runtime behaviour change into a content fix.
Your own tooling lies to you (character counter is wrong, or checks the wrong string).
The manager agent slowly gets more trusting and waves things through because it “looks fine.”
Static rules are bets on foresight. BLACK is my admission that my foresight is limited.
So the line I’ve drawn is:
Freeze the architecture once it’s behaving like this — don’t keep stacking powers onto BLACK.
Keep the world around it current — client scope docs, compliance rules, internal standards.
Audit BLACK regularly — spot‑check argue‑logs, make sure it admits its own tool failures, not just others’.
I don’t want a huge adversarial framework. I want a simple shape that reliably hits things and tells me what broke.
Where this sits relative to “adversarial AI” work
Later I went looking at what smarter people were doing.
Some rough alignments:
Sabotage from Within (LaMAS) looks at multi‑agent teams where one agent turns rogue and sabotages the system, especially in managerial roles. My response is architectural: don’t leave the manager alone at the top. Give it an external adversary, on a different model, whose job is “tell us when we’re lying to ourselves.”openreview
RedDebate uses multi‑agent debates to improve safety — models argue with each other and flag unsafe responses. My shape: not a debate, but a single wide‑scope adversary slamming into patches, then builders + human owner deciding what to do with the shards.openreview
OWASP GenAI Red Teaming focuses on injections, jailbreaks, privilege escalation, bias. I care a lot about that too, but the pattern here is more about self‑deception in production systems: prompts, configs, and humans quietly bending rules and talking themselves into “it’s fine.”genai.owasp+1
What I haven’t seen much of (could be my limited reading) is this exact combo:
It’s a slightly different tool than “try to break the model with spicy prompts.”
Why I’m putting this out there
Honestly? Because I’m mostly doing this alone, and my current network doesn’t care about this stuff.
I’d like to:
Compare notes with people who also care about keeping fleets honest, not just making them clever.
Have other eyes on this pattern in case there’s a hole I haven’t seen yet.
Contribute something concrete from running agents in production, not just talk abstractly about “agentic AI.”
If you’re building or evaluating agent systems and any of this resonates, feel free to steal/adapt it, or tell me where it breaks. I’d rather find out here than from a regulator or a client.