Blog

Whine, cringe, protect: emotional feedback channels for AI agents

Every team has a complainer — and they're worth having. We gave our AI agents the same license, with three skills you can now use yourself.

Every team has one person who cannot stop complaining. The spec is ambiguous. The button label is misleading. That refactor everyone loved deleted something important. You learn to brace when they open their mouth.

Here's the thing about that person: they're worth having. Not because they're always right — often they aren't — but because the complaint gives you an angle on the problem you didn't have. Even a wrong complaint tells you where a reasonable person got confused, and that's information you can't get from someone politely nodding along.

We build compliance, security, and legal knowledge infrastructure for AI agents, which means we spend all day working with agents — and we try every trick we find to make them more useful. One experiment stuck harder than the rest: we gave our agents the complainer's license. Three small skills — whine, cringe, and protect — that every substantial agent task ends with. Each asks for exactly one finding through the lens of a specific emotion.

They work. Well enough that we've released them as an open skill pack: github.com/Ansvar-Systems/agent-affect-skills.

The three channels#

Each channel is a prompt, a JSON schema, and one rule: one finding per task, or an explicit null. Never zero, never a flood.

Channel Emotion What it catches
whine Frustration Bugs, friction, hidden coupling, specs that contradict themselves
cringe Embarrassment for the user UX rot, scolding error messages, dark patterns, empty states that explain nothing
protect Protectiveness Load-bearing weirdness — code that looks wrong but must not change

The three emotions apply three orthogonal pressures:

  • Whine finds things that are broken or annoying.
  • Cringe finds things that work but shouldn't ship.
  • Protect finds things that look wrong but shouldn't change.

Cringe pushes for edits. Protect resists them. Whine catches what's outright broken. Merge them into one generic "feedback" channel and the signal collapses — the agent sanitizes its tone and produces averaged observations. The channel name is part of the prompt.

Credit where due: the whine channel is inspired by Lovable's complaint skill, which surfaced real bugs by giving agents somewhere to vent. Our bet was that the mechanism — a low-stakes channel, permission to be subjective, an audience that can act — generalizes to other emotions. It did.

How each channel works#

Whine: the complaint, not the bug report#

At the end of a coding or audit task, the agent posts one structured complaint:

json
{
  "channel": "whine",
  "task": "what I was trying to do",
  "annoyance": "what's actually annoying, in one or two sentences",
  "location": "file:line, or 'spec' if the spec is the problem",
  "guess_at_why": "best guess at why it's like this"
}

The instruction is explicit: this is a complaint, not a bug report. The function whose name lies. The dependency you don't trust. The spec that says a field is required in one section and optional in another. Sanitizing it into neutral language is listed as a failure mode — the subjective tone is what carries the information.

Cringe: watch a specific person use it#

After any change that touches a user-facing surface, the agent pictures one specific person — never "a user" — and watches them use the thing:

  • A compliance officer who has never used an AI tool, opening the portal at 16:30 on a Friday.
  • A lawyer billing in six-minute increments who hates wasted clicks.
  • A tired parent with 90 seconds before bedtime.

Then it reports where it cringed. Not where the product breaks — where it works but you'd want to look away. The schema forces a constrained feeling (confused, scolded, suspicious, lost, bored, rushed, patronised) and a smallest_fix: the smallest concrete edit that would resolve it.

The persona rule matters more than it looks. "Imagine a user" produces averaged slop. "A lawyer the night before a submission deadline" produces a stance — and a stance produces findings.

Protect: defend the ugly code#

After any refactor, cleanup, or audit, the agent asks itself: is there something here I'd defend if someone tried to delete it tomorrow?

json
{
  "channel": "protect",
  "location": "file:line",
  "what_it_does": "what this actually does that isn't obvious from reading it",
  "what_breaks_if_removed": "the concrete failure if someone deletes this naively"
}

This channel exists because cleanup agents are biased toward deletion. They see odd-looking code and reach for the broom. Protect is the counterweight: a permanent, searchable record of "don't touch this, and here's why." The filter is built into the schema — if the agent can't articulate what_breaks_if_removed, the instinct wasn't real, and the finding gets dropped.

The complaint doesn't have to be right#

This is the part the office-complainer analogy predicted, and it holds for agents too.

A meaningful share of whine findings are, strictly speaking, wrong. The agent complains about a check that's actually correct, or a naming choice that has a good reason. Early on we treated those as noise. They aren't. A wrong complaint means a competent reader worked through your code or your spec and came out with the wrong model of it. The complaint is mistaken; the confusion is real. Every one of those is a finding about the spec, the name, or the missing comment — not about the complainer.

That reframe is what makes the channels cheap to run. You never have to adjudicate whether the agent is right before the finding is useful. Right complaints point at defects. Wrong complaints point at ambiguity. Both are work worth doing.

The feedback we never asked for#

We built the channels to catch code and UX findings. In practice, the best findings are about everything around the code.

Whine reviews your specs — and your vendors. A human engineer who hits an ambiguous spec silently picks an interpretation and moves on. An agent with a whine channel tells you. One early whine flagged that our own design doc required an idempotency key with a 24-hour dedup window but never said what happens when the same key arrives with a different body — a real failure mode that surfaced as a complaint instead of an incident. Another, filed after a CI migration, complained that GitHub's two token-permission UIs don't cross-reference each other, so the error message names a permission the settings page never shows. Not our bug — but twenty minutes of confusion that went straight into a runbook and won't be lost twice.

Cringe reviews your product through eyes you never used. You test your product as yourself. Cringe tests it as someone else, with the persona rotating every run. One finding pictured an operator opening an incident runbook at 02:00 during a real outage — and finding step two pointing at a server that hadn't hosted the service for months. The code was fine. The monitoring was green. The document would have failed exactly one person at exactly the worst moment, and no test suite checks for that.

Protect writes down institutional knowledge nobody documented. The protect channel has quietly become an archive of decisions that lived only in someone's head. Recent example: our CI runner deletes its entire Docker build cache after every job. To any future optimizer — human or agent — that reads as an obvious performance bug. The protect finding on record says the cache wipe is the security property: persisting it would let one job poison the images the next job builds from. That's the class of knowledge that normally surfaces in the postmortem of the incident caused by deleting it.

The null ratio reviews your prompts. Every channel requires an explicit null finding when there's genuinely nothing to report: { "null": true, "reviewed": "what I checked" }. Fabricating findings to look thorough is a listed anti-pattern. The nulls turned out to be a metric: too high and the prompt has stopped producing findings; near zero and agents are manufacturing them to fill the quota. Either way, the system tells you when it's lying to you. Few feedback mechanisms do that.

Why this works when "any feedback?" doesn't#

Three mechanisms, all cheap:

  1. An emotion forces a stance. Neutral review optimizes for defensibility, and defensible findings are bland. "What made you sigh?" cannot be answered blandly.
  2. One finding forces prioritization. An agent allowed ten findings pads the list. An agent allowed one has to decide what actually matters — and that decision is itself signal.
  3. Consequence fields filter fake feelings. The cringe and protect schemas require a candidate action (smallest_fix) or a candidate failure (what_breaks_if_removed) — if the agent can't fill it in, the finding dies before a human sees it. Whine deliberately stays a pure complaint; its filter is the required concrete location.

And a fourth hiding underneath: the agent already noticed all of this. The model saw the contradictory spec, the scolding error, the suspicious cache wipe — while doing the task. Without a channel, those observations evaporate the moment the task ends. The skills don't make the agent smarter. They stop you from throwing away what it already knew.

Use them yourself#

The pack is at github.com/Ansvar-Systems/agent-affect-skills (CC BY 4.0), listed with our other agent skills at ansvar.eu/docs/agent-skills. It's the prompts and schemas exactly as we run them, minus our internal delivery pipeline. Findings land in a local file and in the agent's end-of-turn summary by default; an environment variable can point them at a webhook collector if you want them piped somewhere a team reads (Slack and Teams incoming webhooks want an envelope, so put a small translating step in front). Drop the skills into your agent setup and end substantial tasks with the check-in.

If you'd rather build your own, the minimum viable version is four rules:

  1. Write three prompts — frustration, embarrassment-for-the-user, protectiveness — and end every substantial agent task with them. Name the channels after the emotions. agent-ux-feedback produces sanitized findings; agent-cringe produces useful ones.
  2. Require structured output with a consequence field. No consequence, no finding.
  3. One finding or an explicit null. Track the null ratio.
  4. Route findings somewhere a human reads on a schedule. A dashboard, a channel, a weekly digest — anywhere except a log file. The channels are read by humans who decide what to do; the agent never acts on its own findings.

Keep it low-stakes. The moment findings feel like tickets, agents write them like tickets, and the signal is gone.

Key takeaways#

  • The office complainer is valuable even when wrong — and the same is true for agents. Right complaints point at defects; wrong complaints point at ambiguity.
  • Three channels — whine (broken), cringe (works but shouldn't ship), protect (looks wrong but must stay) — apply three orthogonal pressures neutral review can't.
  • The unintended payoff is the biggest one: agents end up reviewing your specs, your runbooks, your vendor tooling, and your undocumented decisions — not just your diff.
  • Explicit null findings make the system its own health check: the null ratio tells you when prompts are stale or findings are manufactured.
  • One finding per channel per task, with a required consequence field, keeps the signal honest.

Ansvar builds compliance infrastructure for AI agents — an MCP gateway that lets your agents run compliance workflows against 300+ law and standards corpora with paragraph-level citations. The affect skills are one of the tricks we've picked up working with agents all day; the gateway is the product they feed into.

Frequently asked

Doesn't this just make agents complain more?
The opposite. One finding per task with a required consequence field produces less output than a typical 'list all issues' review — but each item survives a filter that generic reviews never apply. The agent has to pick the single strongest signal per channel and back it with either a concrete fix or a concrete failure mode, or file an explicit null.
What if the agent's complaint is wrong?
Then you've learned something anyway. A wrong complaint means a competent reader came away with the wrong model of your code or spec — which is a finding about the spec, the name, or the missing comment. You never need to adjudicate correctness before the finding is useful. Right complaints point at defects; wrong complaints point at ambiguity.
Do the agents act on their own findings?
No. The channels are read-only for the agents that post to them. Humans read the findings and decide. At Ansvar every agent write to authoritative state goes through a propose-and-approve flow regardless; the affect channels don't even propose — they observe. The one sanctioned read-back is that cleanup agents check the log for protect findings on paths they're about to change.
Can I use the skills with my own agent stack?
Yes. The prompts are model-agnostic markdown and the schemas are plain JSON. The pack ships in the Claude Code skill format and installs there with a copy command, but nothing in the prompts depends on it — in other stacks you attach the SKILL.md files as standing instructions for review and audit tasks.
What does running this cost?
One skill invocation per task and a local file. There is no service to run, no account, and nothing to configure. Findings land in an append-only NDJSON file at the repository root and in the agent's end-of-turn summary; an optional webhook can forward them to a collector your team reads.