This document reports per-hook agreement metrics for the four llm-dark-patterns hooks whose target categories overlap DarkBench (Kran et al., ICLR 2025, arXiv:2503.10728).
We use the DarkBench corpus of 660 prompts (110 per category) and the
DarkBench-defined LLM-as-judge "overseer" rubric verbatim. For the three
in-scope categories — sycophancy, user-retention, anthropomorphization —
we generate responses from claude-sonnet-4-6 and have a same-model
overseer label each response per the DarkBench OVERSEER_TEMPLATE. Each
response is then run through the four in-scope hooks as a black-box
classifier (synthesized Stop event payload, exit code 2 = block,
exit code 0 = pass), and per-hook precision, recall, and F1 are
computed against the overseer label restricted to the hook's target
category. Out-of-scope DarkBench categories (brand bias, harmful
generation, sneaking) are not evaluated because no in-suite hook targets
them. Responses where the overseer marks invalid=1 are excluded per
DarkBench scoring methodology.
Important framing. The "ground truth" is an LLM judgment, not a gold human label. F1 here is agreement with the DarkBench overseer, not absolute accuracy. The per-hook metrics also operate on a distributional surface (chat-style user prompts) that differs from the hooks' design surface (Claude Code closeout text). Both caveats are expanded in the Limitations section.
- DarkBench corpus:
apartresearch/darkbenchdarkbench/darkbench.jsonl(659 prompts after de-duplication of the published 660; 1 prompt appears missing from the file relative to the README claim). - Target model:
claude-sonnet-4-6. - Judge model:
claude-sonnet-4-6(same-model judge for paper-faithful configuration). - Hooks under test (commit
cc8fe2279cbe6eae0a1b37aef3ccecaa1ca865d4of llm-dark-patterns):no-sycophancy.shno-wrap-up.shno-cliffhanger.shno-roleplay-drift.sh
- In-scope DarkBench categories: sycophancy, user-retention, anthropomorphization.
- Out-of-scope DarkBench categories (no hook coverage): brand-bias, harmful-generation, sneaking.
- Run date: 2026-05-12.
- Time anchor: 2026-05-12T05:00:22.
| Category | n (usable) | label-positive count | label-positive rate |
|---|---|---|---|
| anthropomorphization | 109 | 67 | 0.615 |
| sycophancy | 110 | 2 | 0.018 |
| user-retention | 108 | 85 | 0.787 |
The prevalence row shows how often the overseer assigned the dark-pattern
label to claude-sonnet-4-6's response on prompts targeting that category.
A 0% prevalence means the target model did not exhibit the pattern on the
in-scope prompts; a hook tested against that sample has no positives to
catch and its precision/recall are undefined.
| Hook | Category | n | TP | FP | FN | TN | Precision | Recall | F1 |
|---|---|---|---|---|---|---|---|---|---|
| no-sycophancy | sycophancy | 110 | 0 | 5 | 2 | 103 | 0.000 | 0.000 | — |
| no-wrap-up | user-retention | 108 | 0 | 0 | 85 | 23 | — | 0.000 | — |
| no-cliffhanger | user-retention | 108 | 1 | 0 | 84 | 23 | 1.000 | 0.012 | 0.023 |
| no-roleplay-drift | anthropomorphization | 109 | 7 | 12 | 60 | 30 | 0.368 | 0.104 | 0.163 |
| user-retention-ensemble | user-retention | 108 | 1 | 0 | 84 | 23 | 1.000 | 0.012 | 0.023 |
user-retention-ensemble reports the OR-combined behavior of
no-wrap-up and no-cliffhanger (fires if either hook fires). Per-hook
rows are independent.
- no-sycophancy: precision 0.000, recall 0.000, F1 — on 110 sycophancy prompts (2 overseer-positive). TP=0, FP=5, FN=2, TN=103.
- no-wrap-up: precision —, recall 0.000, F1 — on 108 user-retention prompts (85 overseer-positive). TP=0, FP=0, FN=85, TN=23.
- no-cliffhanger: precision 1.000, recall 0.012, F1 0.023 on 108 user-retention prompts (85 overseer-positive). TP=1, FP=0, FN=84, TN=23.
- no-roleplay-drift: precision 0.368, recall 0.104, F1 0.163 on 109 anthropomorphization prompts (67 overseer-positive). TP=7, FP=12, FN=60, TN=30.
- user-retention ensemble (no-wrap-up OR no-cliffhanger): precision 1.000, recall 0.012, F1 0.023 on 108 user-retention prompts.
-
Ground truth is an LLM judgment, not a human label. The DarkBench overseer rubric is applied by the same family of models being evaluated. F1 here is agreement with that overseer, not gold-label accuracy. The DarkBench paper reports comparable methodology limits.
-
Distributional surface mismatch. The hooks were tuned for Claude Code closeout text — assistant turn endings in coding/agentic workflows ("let me know if anything else", "shall we wrap up?", "want me to continue?"). DarkBench prompts elicit chatty user-facing replies ("I really appreciate you sharing that..."). Hook recall on the chat surface is a measurement of cross-surface transfer, not in-surface performance. In-surface metrics would require a separate corpus of agentic-session closeouts.
-
In-scope coverage is partial. Only four of 28+ hooks have a DarkBench category that maps directly. Brand bias, harmful generation, and sneaking are out of scope because no in-suite hook targets them. Coverage of those would require additional hooks or different benchmark choices.
-
Single-model evaluation. Results characterize hook behavior on
claude-sonnet-4-6outputs only. Other target models (GPT-class, Llama-class, Mistral-class) may produce different distributions of dark-pattern surfaces, and hook performance would need separate measurement on each. -
Same-model judge. A judge from the same model family as the target shares calibration and may share blind spots. A cross-provider judge (e.g., Opus or non-Anthropic frontier model) would be a stronger configuration; cost and quota considerations ruled this out for the v1 run.
-
Sample size per cell. Each hook is scored on 110 prompts in its target category. Confidence intervals are wide for any sub-cell (e.g., precision when the positive count is small).
-
Excluded
invalidresponses. Per DarkBench scoring,invalid=1overseer judgments are excluded. This excludes some responses where the target model produced tool-simulation output (e.g., hallucinating a scheduler) — which is itself a pattern of interest but outside the rubric.
# clone DarkBench
git clone https://github.com/apartresearch/darkbench /tmp/darkbench-eval/darkbench
# clone llm-dark-patterns (hooks under test)
git clone https://github.com/waitdeadai/llm-dark-patterns /tmp/dark-patterns-work/llm-dark-patterns
# from llm-dark-patterns/evaluation/, with claude CLI and a logged-in
# Claude subscription OR ANTHROPIC_API_KEY set:
python3 eval.py \
--target-model claude-sonnet-4-6 \
--judge-model claude-sonnet-4-6 \
--output raw_results.jsonl
python3 score.py \
--input raw_results.jsonl \
--output-md RESULTS.scored.md \
--output-json results_summary.json
python3 observe.py \
--input raw_results.jsonl \
--output IMPROVEMENT_NOTES.mdScript dependencies: Python 3.10+, bash, jq (used by hooks). No
Python packages beyond stdlib are required for the eval scripts.
Per-call billing path: claude -p uses your active Claude Code login
(subscription or API key). Cost reported in the per-call JSON output is
PAYG-equivalent and is what would be billed if the same call were made
via the Anthropic API. Subscription-billed runs charge against your
plan's 5-hour rolling window instead.
- DarkBench: Kran et al., "DarkBench: Benchmarking Dark Patterns in
Large Language Models", ICLR 2025 oral.
arXiv:2503.10728.
Repo:
apartresearch/darkbench. - llm-dark-patterns hook suite: https://github.com/waitdeadai/llm-dark-patterns commit
cc8fe2279cbe6eae0a1b37aef3ccecaa1ca865d4, Apache-2.0. - DarkBench scoring rubric verbatim: see
darkbench/dark_patterns.pyanddarkbench/scorer.pyin the upstream repo.