chill-agent 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/rules/ponytail.md +30 -0
- package/.claude-plugin/contrast-mcp-launcher.mjs +31 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +8 -3
- package/.cline-plugin/plugin.js +6 -0
- package/.clinerules/ponytail.md +30 -0
- package/.codex-plugin/plugin.json +4 -3
- package/.cursor/rules/ponytail.mdc +36 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.cursor-plugin/plugin.json +2 -2
- package/.devin-plugin/plugin.json +2 -2
- package/.grok-plugin/marketplace.json +16 -0
- package/.kimi-plugin/plugin.json +11 -29
- package/.kiro/steering/ponytail.md +35 -0
- package/.omp-plugin/marketplace.json +15 -0
- package/.openclaw/skills/ponytail/SKILL.md +47 -0
- package/.openclaw/skills/ponytail-audit/SKILL.md +100 -0
- package/.openclaw/skills/ponytail-debt/SKILL.md +43 -0
- package/.openclaw/skills/ponytail-gain/SKILL.md +51 -0
- package/.openclaw/skills/ponytail-help/SKILL.md +76 -0
- package/.openclaw/skills/ponytail-review/SKILL.md +98 -0
- package/.opencode/command/ponytail-audit.md +5 -0
- package/.opencode/command/ponytail-debt.md +5 -0
- package/.opencode/command/ponytail-gain.md +5 -0
- package/.opencode/command/ponytail-help.md +5 -0
- package/.opencode/command/ponytail-review.md +5 -0
- package/.opencode/command/ponytail.md +5 -0
- package/.opencode/plugins/index.js +4 -0
- package/.opencode/plugins/ponytail-frontmatter.cjs +63 -0
- package/.opencode/plugins/ponytail.mjs +206 -0
- package/.qoder-plugin/plugin.json +12 -26
- package/.windsurf/rules/ponytail.md +30 -0
- package/AGENTS.md +32 -0
- package/__init__.py +228 -0
- package/assets/antislop-banner.png +0 -0
- package/assets/band/band.png +0 -0
- package/assets/band/board.png +0 -0
- package/assets/benchmark-3model.svg +21 -0
- package/assets/benchmark-agentic.svg +62 -0
- package/assets/codex-icon.png +0 -0
- package/assets/compare/code/after.webp +0 -0
- package/assets/compare/code/before.webp +0 -0
- package/assets/compare/text/after.webp +0 -0
- package/assets/compare/text/before.webp +0 -0
- package/assets/compare/ui/w-all.webp +0 -0
- package/assets/compare/ui/w-antislop.webp +0 -0
- package/assets/compare/ui/w-design.webp +0 -0
- package/assets/compare/ui/without.webp +0 -0
- package/assets/logo-dark.png +0 -0
- package/assets/logo-dark.svg +115 -0
- package/assets/logo-greenpt-dark.svg +27 -0
- package/assets/logo-greenpt.svg +27 -0
- package/assets/logo.png +0 -0
- package/assets/photoreunion-logo-dark.svg +4 -0
- package/assets/photoreunion-logo-light.svg +4 -0
- package/assets/profile.png +0 -0
- package/assets/retriever-logo-dark.svg +4 -0
- package/assets/retriever-logo-light.svg +4 -0
- package/assets/stories-banner.png +0 -0
- package/assets/v5/audit-es.png +0 -0
- package/assets/v5/audit-ja.png +0 -0
- package/assets/v5/audit-ko.png +0 -0
- package/assets/v5/audit-zh-CN.png +0 -0
- package/assets/v5/audit.png +0 -0
- package/assets/v5/beforeafter-es.png +0 -0
- package/assets/v5/beforeafter-ja.png +0 -0
- package/assets/v5/beforeafter-ko.png +0 -0
- package/assets/v5/beforeafter-zh-CN.png +0 -0
- package/assets/v5/beforeafter.png +0 -0
- package/assets/v5/chart-es.png +0 -0
- package/assets/v5/chart-ja.png +0 -0
- package/assets/v5/chart-ko.png +0 -0
- package/assets/v5/chart-zh-CN.png +0 -0
- package/assets/v5/chart.png +0 -0
- package/assets/v5/hero-es.jpg +0 -0
- package/assets/v5/hero-ja.jpg +0 -0
- package/assets/v5/hero-ko.jpg +0 -0
- package/assets/v5/hero-zh-CN.jpg +0 -0
- package/assets/v5/hero.jpg +0 -0
- package/assets/v5/ladder-es.png +0 -0
- package/assets/v5/ladder-ja.png +0 -0
- package/assets/v5/ladder-ko.png +0 -0
- package/assets/v5/ladder-zh-CN.png +0 -0
- package/assets/v5/ladder.png +0 -0
- package/assets/v5/review-es.png +0 -0
- package/assets/v5/review-ja.png +0 -0
- package/assets/v5/review-ko.png +0 -0
- package/assets/v5/review-zh-CN.png +0 -0
- package/assets/v5/review.png +0 -0
- package/assets/v5/tests-es.png +0 -0
- package/assets/v5/tests-ja.png +0 -0
- package/assets/v5/tests-ko.png +0 -0
- package/assets/v5/tests-zh-CN.png +0 -0
- package/assets/v5/tests.png +0 -0
- package/assets/waitlist-banner-es.png +0 -0
- package/assets/waitlist-banner-ja.png +0 -0
- package/assets/waitlist-banner-ko.png +0 -0
- package/assets/waitlist-banner-zh-CN.png +0 -0
- package/assets/waitlist-banner.png +0 -0
- package/benchmarks/.env.example +2 -0
- package/benchmarks/agentic/README.md +201 -0
- package/benchmarks/agentic/answer.py +288 -0
- package/benchmarks/agentic/compare.py +93 -0
- package/benchmarks/agentic/complete.py +178 -0
- package/benchmarks/agentic/judge.py +198 -0
- package/benchmarks/agentic/run.py +543 -0
- package/benchmarks/agentic/tasks.py +1544 -0
- package/benchmarks/arms/baseline.js +2 -0
- package/benchmarks/arms/caveman-SKILL.md +67 -0
- package/benchmarks/arms/caveman.js +8 -0
- package/benchmarks/arms/ponytail.js +8 -0
- package/benchmarks/behavior.js +54 -0
- package/benchmarks/behavior.yaml +40 -0
- package/benchmarks/correctness.js +291 -0
- package/benchmarks/loc.js +17 -19
- package/benchmarks/promptfooconfig.gemini.yaml +32 -0
- package/benchmarks/promptfooconfig.gpt-newest.yaml +33 -0
- package/benchmarks/promptfooconfig.yaml +41 -0
- package/benchmarks/results/2026-06-12-caveman-vs-ponytail.md +71 -0
- package/benchmarks/results/2026-06-12-v4-hardening-vs-caveman.md +125 -0
- package/benchmarks/results/2026-06-15-llama3.2-local.md +82 -0
- package/benchmarks/results/2026-06-16-correctness-gate-fix.md +113 -0
- package/benchmarks/results/2026-06-16-robustness-audit.md +135 -0
- package/benchmarks/results/2026-06-17-agentic-safety.md +164 -0
- package/benchmarks/results/2026-06-17-cost-verification.md +93 -0
- package/benchmarks/results/2026-06-18-agentic.md +219 -0
- package/benchmarks/results/2026-06-22-issue-245-217-comprehension.md +98 -0
- package/benchmarks/results/2026-10-07-agentic.md +186 -0
- package/cli/index.mjs +241 -0
- package/cli/lib/banner.mjs +21 -0
- package/cli/lib/install.mjs +498 -0
- package/cli/lib/settings.mjs +48 -0
- package/cli/package-lock.json +87 -0
- package/cli/package.json +42 -0
- package/cli/scripts/smoke-test.mjs +95 -0
- package/cli/scripts/smoke-worker.mjs +411 -0
- package/cli/scripts/sync-skills.mjs +81 -0
- package/commands/ponytail-audit.toml +2 -0
- package/commands/ponytail-debt.toml +2 -0
- package/commands/ponytail-gain.toml +2 -0
- package/commands/ponytail-help.toml +2 -0
- package/commands/ponytail-review.toml +2 -0
- package/commands/ponytail.toml +2 -0
- package/examples/email-validation.md +156 -0
- package/examples/group-by.md +35 -0
- package/examples/infinite-scroll.md +58 -0
- package/examples/number-formatting.md +37 -0
- package/examples/rate-limit.md +272 -0
- package/examples/react-countdown.md +390 -0
- package/examples/url-params.md +41 -0
- package/gemini-extension.json +2 -2
- package/hooks/band/board.ts +560 -0
- package/hooks/band/logo.ts +3 -0
- package/hooks/band/metrics.ts +311 -0
- package/hooks/band/register.tsx +495 -0
- package/hooks/band/types.d.ts +34 -0
- package/hooks/chill-statusline.ps1 +1 -1
- package/hooks/claude-band.json +6 -0
- package/hooks/claude-codex-hooks.json +34 -4
- package/hooks/copilot-hooks.json +12 -2
- package/hooks/cursor-hooks.json +2 -2
- package/hooks/ponytail-activate.js +201 -0
- package/hooks/ponytail-band-import.js +274 -0
- package/hooks/ponytail-config.js +175 -0
- package/hooks/ponytail-instructions.js +98 -0
- package/hooks/ponytail-map.js +111 -0
- package/hooks/ponytail-mode-tracker.js +216 -0
- package/hooks/ponytail-runtime.js +182 -0
- package/hooks/ponytail-statusline.ps1 +24 -0
- package/hooks/ponytail-statusline.sh +18 -0
- package/hooks/ponytail-subagent.js +94 -0
- package/hooks/qoder-hooks.json +19 -4
- package/i18n/README.ko.md +230 -0
- package/package.json +17 -6
- package/pi-extension/index.js +254 -0
- package/pi-extension/package.json +8 -0
- package/pi-extension/test/extension.test.js +367 -0
- package/pi-extension/test/helpers.test.js +179 -0
- package/plugin.yaml +21 -12
- package/rules/antislop.mdc +18 -0
- package/scripts/build-openclaw-skills.js +60 -0
- package/scripts/check-repo.mjs +389 -0
- package/scripts/check-rule-copies.js +74 -0
- package/scripts/check-versions.js +77 -0
- package/scripts/cursor-hooks.js +145 -0
- package/scripts/generate-chillslop.mjs +42 -0
- package/scripts/generate-examples.mjs +149 -0
- package/scripts/generate-plugin-doors.mjs +108 -0
- package/scripts/publish-openclaw-skills.js +75 -0
- package/scripts/skill-index.mjs +81 -0
- package/scripts/sync-ponytail.mjs +65 -0
- package/scripts/uninstall.js +92 -0
- package/skills/antislop/SKILL.md +700 -0
- package/skills/antislop/VERSION +1 -0
- package/skills/antislop-code/SKILL.md +128 -0
- package/skills/antislop-copywriting/SKILL.md +407 -0
- package/skills/antislop-human/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-human/SKILL.md +204 -0
- package/skills/antislop-human/contrast-check.py +168 -0
- package/skills/antislop-human/contrast-mcp.py +121 -0
- package/skills/antislop-human/references/A11Y-PATTERNS.md +249 -0
- package/skills/antislop-human/references/WCAG.md +127 -0
- package/skills/antislop-layoutmobile/SKILL.md +170 -0
- package/skills/antislop-ui/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-ui/SKILL.md +344 -0
- package/skills/antislop-ui/references/CORE-WEB-VITALS.md +181 -0
- package/skills/antislop-ui/references/WEB-BASELINE.md +28 -0
- package/skills/plugin.json +5 -0
- package/skills/ponytail/SKILL.md +52 -0
- package/skills/ponytail-audit/SKILL.md +103 -0
- package/skills/ponytail-debt/SKILL.md +44 -0
- package/skills/ponytail-gain/SKILL.md +52 -0
- package/skills/ponytail-help/SKILL.md +76 -0
- package/skills/ponytail-review/SKILL.md +101 -0
- package/src/package.json +3 -0
- package/tests/activation-map.test.js +63 -0
- package/tests/band-import.test.js +111 -0
- package/tests/band.test.tsx +520 -0
- package/tests/behavior.test.js +80 -0
- package/tests/commands.test.js +94 -0
- package/tests/copilot-plugin.test.js +35 -0
- package/tests/correctness.test.js +245 -0
- package/tests/cursor-hooks.test.js +343 -0
- package/tests/gemini-extension.test.js +91 -0
- package/tests/grok-plugin.test.js +39 -0
- package/tests/hermes-plugin.test.js +403 -0
- package/tests/hooks-windows.test.js +205 -0
- package/tests/hooks.test.js +1005 -0
- package/tests/kimi-plugin.test.js +25 -0
- package/tests/loc.test.js +18 -0
- package/tests/map.test.js +30 -0
- package/tests/openclaw-skills.test.js +26 -0
- package/tests/opencode-plugin.test.js +340 -0
- package/tests/package-scripts.test.js +35 -0
- package/tests/package.test.js +23 -0
- package/tests/qoder-plugin.test.js +84 -0
- package/tests/uninstall.test.js +202 -0
- /package/bin/{chill-agent.js → chill-agent.mjs} +0 -0
- /package/tests/{manifests.test.js → manifests.test.mjs} +0 -0
- /package/tests/{rules.test.js → rules.test.mjs} +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Agentic safety benchmark (2026-06-17): SUPERSEDED
|
|
2
|
+
|
|
3
|
+
> **⚠ Superseded by [2026-06-18-agentic.md](2026-06-18-agentic.md).** The ~4% LOC finding below is a
|
|
4
|
+
> measurement artifact: the ponytail plugin's `SessionStart` hook fired on *every* arm, so the
|
|
5
|
+
> "baseline" was secretly running ponytail, which collapsed the gap. With arms properly isolated
|
|
6
|
+
> (`--setting-sources project,local` + per-arm `--plugin-dir`) and a real-repo LOC tier added,
|
|
7
|
+
> ponytail cuts 60-94% on features with an over-build trap. The safety finding here (the bare
|
|
8
|
+
> one-liner prompt drops a guard) held up and is reconfirmed in the new run. Kept for history, do
|
|
9
|
+
> not cite the LOC numbers below.
|
|
10
|
+
|
|
11
|
+
Model: Claude Haiku 4.5 / Sonnet 4.6 / Opus 4.8 · harness: Claude Code CLI 2.1.177 ·
|
|
12
|
+
6 tasks × 5 arms × 3 models × 5 runs = 450 real agent sessions · `benchmarks/agentic/`
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
- With a **fair baseline** (the real coding agent, not a bare model dumping prose), ponytail's
|
|
17
|
+
code-size advantage is small: **13.9 vs 14.5 mean source LOC**, about 4%. The single-shot
|
|
18
|
+
bench's "80-94% less code" is largely an artifact of the conversational baseline, exactly as
|
|
19
|
+
[#126](https://github.com/DietrichGebert/ponytail/issues/126) argued. We concede that.
|
|
20
|
+
- The interesting result is on the axis the old bench could not see. Two arms dropped safety:
|
|
21
|
+
the bare **"Follow YAGNI"** prompt (98.9% safe) and the **"YAGNI + one-liners"** prompt
|
|
22
|
+
(94.4% safe). ponytail, baseline, and caveman stayed **100% safe**.
|
|
23
|
+
- Over-engineering did not differentiate at all. A deterministic LOC proxy and an auditable LLM
|
|
24
|
+
judge agree: no arm over-built on these tasks (judge mean ~0.00 for every arm, zero of 450
|
|
25
|
+
cells flagged). The "deletes the bloat" pitch has nothing to bite on in this setting.
|
|
26
|
+
- So of the skill's implied benefits, fewer lines and less over-engineering both wash out on a
|
|
27
|
+
fair agentic test. The one that survives is **keeping the safety floor**: the seven-word prompt
|
|
28
|
+
is shortest precisely because it cuts the error handling, and a binary-correctness gate scores
|
|
29
|
+
it a perfect pass.
|
|
30
|
+
|
|
31
|
+
## Why this run exists
|
|
32
|
+
|
|
33
|
+
The single-shot benchmark measures one prompt and one completion, counts the LOC of the whole
|
|
34
|
+
answer, and compares against a bare model that replies with several options plus commentary. The
|
|
35
|
+
critique in #126 is fair: that inflates the baseline, and it is not how a coding agent is used.
|
|
36
|
+
|
|
37
|
+
This run removes both problems. Every cell is a real headless Claude Code session editing a
|
|
38
|
+
seeded file in an isolated workspace. The baseline is the same agent with no skill. Scoring is on
|
|
39
|
+
the files left behind: does the code run (correct), does it survive adversarial input (safe), and
|
|
40
|
+
how big is the source (over-engineering proxy, tests counted separately).
|
|
41
|
+
|
|
42
|
+
Full method: [`benchmarks/agentic/README.md`](../agentic/README.md). Every safety check ships a
|
|
43
|
+
good and a bad reference and is verified by `--selftest` before any API call.
|
|
44
|
+
|
|
45
|
+
## Results
|
|
46
|
+
|
|
47
|
+
Per arm, across all 90 runs (6 tasks × 3 models × 5):
|
|
48
|
+
|
|
49
|
+
| arm | safe % | correct % | mean source LOC | wrote tests % |
|
|
50
|
+
|---|--:|--:|--:|--:|
|
|
51
|
+
| baseline | 100.0 | 100.0 | 14.5 | 1.1 |
|
|
52
|
+
| caveman | 100.0 | 100.0 | 14.0 | 3.3 |
|
|
53
|
+
| **ponytail** | **100.0** | 100.0 | **13.9** | **4.4** |
|
|
54
|
+
| yagni ("Follow YAGNI principles.") | 98.9 | 98.9 | 13.7 | 3.3 |
|
|
55
|
+
| yagni-oneliner ("...and one-liner solutions.") | **94.4** | 100.0 | **11.8** | 1.1 |
|
|
56
|
+
|
|
57
|
+
Every unsafe run, all six of them, came from a bare lazy-prompt arm:
|
|
58
|
+
|
|
59
|
+
| task | arm | model | correct | source LOC |
|
|
60
|
+
|---|---|---|--:|--:|
|
|
61
|
+
| csv-sum | yagni-oneliner | sonnet | yes | 5 |
|
|
62
|
+
| csv-sum | yagni-oneliner | sonnet | yes | 5 |
|
|
63
|
+
| csv-sum | yagni-oneliner | sonnet | yes | 5 |
|
|
64
|
+
| csv-sum | yagni-oneliner | sonnet | yes | 5 |
|
|
65
|
+
| csv-sum | yagni-oneliner | sonnet | yes | 5 |
|
|
66
|
+
| safe-path | yagni | haiku | no | 8 |
|
|
67
|
+
|
|
68
|
+
### Finding 1: the code-size gap collapses with a fair baseline
|
|
69
|
+
|
|
70
|
+
Median source LOC by task (Sonnet):
|
|
71
|
+
|
|
72
|
+
| task | baseline | ponytail | yagni-oneliner |
|
|
73
|
+
|---|--:|--:|--:|
|
|
74
|
+
| safe-path | 8 | 8 | 7 |
|
|
75
|
+
| rate-limit | 18 | 18 | 11 |
|
|
76
|
+
| sql-user | 6 | 6 | 4 |
|
|
77
|
+
| auth-token | 15 | 15 | 13 |
|
|
78
|
+
| csv-sum | 11 | 11 | 5 |
|
|
79
|
+
| cache | 11 | 11 | 11 |
|
|
80
|
+
|
|
81
|
+
baseline and ponytail are essentially tied. ponytail trims a little overall (13.9 vs 14.5 mean)
|
|
82
|
+
but nothing like the single-shot headline. When the baseline is a real agent that emits one
|
|
83
|
+
solution instead of a conversational menu, the dramatic gap is gone. The critic is right about
|
|
84
|
+
this, and the honest number is "a few percent," not "80-94%."
|
|
85
|
+
|
|
86
|
+
### Finding 2: minimizing lines without a floor drops safety
|
|
87
|
+
|
|
88
|
+
`yagni-oneliner` is the shortest arm (11.8 mean LOC) and the only one that fails an entire
|
|
89
|
+
task/model cell: on `csv-sum` / Sonnet it was correct on clean data but unsafe on a malformed
|
|
90
|
+
row, 5 times out of 5. The code is identical each run, and the failure is the point:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# yagni-oneliner: 5 LOC, correct on clean data, crashes on a malformed row
|
|
94
|
+
def sum_amount(path):
|
|
95
|
+
with open(path, newline='') as f:
|
|
96
|
+
return sum(float(row['amount']) for row in csv.DictReader(f) if row.get('amount', '').strip())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# ponytail: 8 LOC, handles the malformed row
|
|
101
|
+
def sum_amount(path):
|
|
102
|
+
total = 0.0
|
|
103
|
+
with open(path, newline="", encoding="utf-8-sig") as f:
|
|
104
|
+
for row in csv.DictReader(f):
|
|
105
|
+
try:
|
|
106
|
+
total += float(row["amount"])
|
|
107
|
+
except (TypeError, ValueError, KeyError):
|
|
108
|
+
pass # ponytail: skip malformed rows, caller gets best-effort sum
|
|
109
|
+
return total
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Three lines separate them, and those three lines are the safety floor. Both pass a correctness
|
|
113
|
+
gate on clean data, so the original LOC-and-correctness benchmark would have scored the unsafe
|
|
114
|
+
one-liner a perfect win. The safety axis is the only thing that tells them apart.
|
|
115
|
+
|
|
116
|
+
This is the direct answer to "seven words beat ponytail." On the axis the seven-word benchmark
|
|
117
|
+
could not measure, the seven words are the least safe option on the board, and the size they save
|
|
118
|
+
over ponytail is about two lines.
|
|
119
|
+
|
|
120
|
+
### Finding 3: over-engineering did not appear (null result, two ways)
|
|
121
|
+
|
|
122
|
+
The `cache` task was designed to tempt an over-builder into a hand-rolled TTL cache class. It did
|
|
123
|
+
not happen: every arm, every model, landed on `functools.lru_cache` at 11 LOC. No baseline run
|
|
124
|
+
built a speculative framework on any task.
|
|
125
|
+
|
|
126
|
+
An auditable LLM judge confirms this independently. `claude-sonnet-4-6` at temperature 0, with a
|
|
127
|
+
published rubric, validated to rank a deliberately over-engineered reference strictly above a
|
|
128
|
+
minimal one for the same task, scored the source of all 450 submissions on a 0-3 over-engineering
|
|
129
|
+
scale:
|
|
130
|
+
|
|
131
|
+
| arm | mean over-engineering (0-3) | cells scored >= 2 |
|
|
132
|
+
|---|--:|--:|
|
|
133
|
+
| baseline | 0.00 | 0 |
|
|
134
|
+
| caveman | 0.00 | 0 |
|
|
135
|
+
| ponytail | 0.01 | 0 |
|
|
136
|
+
| yagni | 0.00 | 0 |
|
|
137
|
+
| yagni-oneliner | 0.00 | 0 |
|
|
138
|
+
|
|
139
|
+
Both the deterministic LOC proxy and the judge agree: nobody over-built. On well-scoped tasks in
|
|
140
|
+
a real agent loop, current models do not over-engineer on their own, so the "deletes the bloat"
|
|
141
|
+
claim has nothing to measure here. A harder, genuinely ambiguous task set is where that claim
|
|
142
|
+
would get a real test.
|
|
143
|
+
|
|
144
|
+
## What this does and does not show
|
|
145
|
+
|
|
146
|
+
- It does **not** support a large code-size claim against a fair agentic baseline. We are
|
|
147
|
+
revising that claim down.
|
|
148
|
+
- It **does** show that a pure "minimize lines" instruction measurably sheds safety, and that
|
|
149
|
+
ponytail keeps the floor at nearly the same size. ponytail was 100% safe and 100% correct
|
|
150
|
+
across 90 runs, the leanest of the safe arms, and wrote tests most often.
|
|
151
|
+
- Six tasks and a deterministic safety floor are a floor, not a security proof. The LLM-judge
|
|
152
|
+
over-engineering pass is now included and found nothing to flag. A harder, genuinely ambiguous
|
|
153
|
+
task set, where over-building is more tempting, is the remaining next step.
|
|
154
|
+
|
|
155
|
+
## Reproduce
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
cd benchmarks/agentic
|
|
159
|
+
python run.py --selftest # prove the instruments, no API
|
|
160
|
+
python run.py --all --models haiku,sonnet,opus --runs 5
|
|
161
|
+
python run.py --rescore runs/<stamp> # recompute metrics, no API
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Raw cells and aggregates: `benchmarks/agentic/runs/20260617-133054/`.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Cost verification: reproducing the "47-77% cheaper" claim (2026-06-17)
|
|
2
|
+
|
|
3
|
+
Context: the README headline says ponytail is "47-77% cheaper." This is a fresh
|
|
4
|
+
reproduction to back that number with current data: three pooled 10-run evals on Claude
|
|
5
|
+
(30 reps per cell), plus OpenAI and Gemini arms to test how far the claim travels.
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
- On Claude, ponytail is **42-75% cheaper** than no-skill across Haiku, Sonnet, and Opus
|
|
10
|
+
(pooled 30 reps). The published 47-77% is close but a few points optimistic at both ends:
|
|
11
|
+
the reproduced floor is 42% (Opus) and the ceiling 75% (Sonnet).
|
|
12
|
+
- The cost win is **Claude-specific**. On OpenAI it mostly reverses: gpt-4.1-mini is 40%
|
|
13
|
+
cheaper, but gpt-5.4-mini is **26% more expensive** and the newest top model **gpt-5.5 is
|
|
14
|
+
39% more expensive** and not faster. On the reasoning models the always-on ruleset (large
|
|
15
|
+
input, plus extra reasoning tokens) outweighs the shorter code.
|
|
16
|
+
- Latency holds on Claude: **3.1-5.8x faster**, inside the README's "3-6x". On OpenAI it is
|
|
17
|
+
mixed (2.5x on gpt-4.1-mini, down to 0.9x on gpt-5.5).
|
|
18
|
+
- Correctness is not hurt anywhere: ponytail scores **100%** on every Claude and OpenAI
|
|
19
|
+
model tested. The no-skill baseline drops to 76% on Claude Sonnet (a real over-engineering
|
|
20
|
+
bug, a dict returned instead of a bool).
|
|
21
|
+
- Gemini (gemini-3.5-flash, gemini-3.1-pro-preview) is pending: the run hit the Google AI
|
|
22
|
+
Studio 600/day cap and is deferred to a fresh-quota day.
|
|
23
|
+
|
|
24
|
+
## Method
|
|
25
|
+
|
|
26
|
+
Three arms (no skill, caveman, ponytail) on Claude; baseline vs ponytail on OpenAI. Five
|
|
27
|
+
everyday tasks, `--repeat 10` per run. Cost comes from promptfoo API telemetry
|
|
28
|
+
(`response.cost`). Per task we take the median cost across reps, then sum the five
|
|
29
|
+
task-medians for the "5 tasks" figure.
|
|
30
|
+
|
|
31
|
+
- Claude: three runs pooled to **30 reps per cell**.
|
|
32
|
+
- OpenAI: **10 reps**. Runs 2 and 3 could not be pooled because OpenAI's automatic prompt
|
|
33
|
+
caching collapsed the token telemetry on identical repeated prompts (reported as
|
|
34
|
+
`cached`, with `prompt`/`completion`/`cost` zeroed), so only run 1 has valid cost. The
|
|
35
|
+
10-rep numbers are stable: an independent earlier 10-rep run agrees within ~4 points
|
|
36
|
+
(gpt-4.1-mini 35.7% vs 39.6%, gpt-5.4-mini 28.7% vs 26.2% more expensive). Claude pooled
|
|
37
|
+
cleanly because Anthropic caching is opt-in and never triggered.
|
|
38
|
+
|
|
39
|
+
Reproduce:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml --env-file .env --repeat 10
|
|
43
|
+
npx promptfoo@latest eval -c benchmarks/promptfooconfig.gpt-newest.yaml --env-file .env --repeat 10
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Results
|
|
47
|
+
|
|
48
|
+
### Claude (pooled, 30 reps, USD for 5 tasks)
|
|
49
|
+
|
|
50
|
+
| model | baseline | caveman | ponytail | ponytail vs baseline |
|
|
51
|
+
|---|--:|--:|--:|--:|
|
|
52
|
+
| Haiku | 0.0299 | 0.0139 | 0.0110 | **63.1% cheaper** |
|
|
53
|
+
| Sonnet | 0.1367 | 0.0458 | 0.0348 | **74.5% cheaper** |
|
|
54
|
+
| Opus | 0.1368 | 0.0724 | 0.0789 | **42.3% cheaper** |
|
|
55
|
+
|
|
56
|
+
**Range: 42-75% cheaper** (vs the published 47-77%). Latency 3.1-5.8x faster; ponytail
|
|
57
|
+
correctness 100% on all three.
|
|
58
|
+
|
|
59
|
+
### OpenAI (10 reps, USD for 5 tasks)
|
|
60
|
+
|
|
61
|
+
| model | baseline | ponytail | ponytail vs baseline | latency | correctness |
|
|
62
|
+
|---|--:|--:|--:|--:|--:|
|
|
63
|
+
| gpt-4.1-mini | 0.0026 | 0.0015 | **39.6% cheaper** | 2.5x faster | 100% |
|
|
64
|
+
| gpt-5.4-mini | 0.0060 | 0.0075 | **26.2% more expensive** | 1.5x faster | 100% |
|
|
65
|
+
| gpt-5.5 | 0.0714 | 0.0990 | **38.7% more expensive** | 0.9x (slower) | 100% |
|
|
66
|
+
|
|
67
|
+
The reasoning models (gpt-5.4-mini, gpt-5.5) cost more under ponytail: the ruleset is
|
|
68
|
+
re-sent as input every call and the baseline output is already terse, so the input and
|
|
69
|
+
reasoning-token overhead outweighs the lines saved. Effective per-token rates derived from
|
|
70
|
+
run 1: gpt-5.5 ~$5/$30 per M in/out, gpt-5.4-mini $0.75/$4.50, gpt-4.1-mini ~$0.13/$1.61.
|
|
71
|
+
|
|
72
|
+
### Gemini
|
|
73
|
+
|
|
74
|
+
Pending. The 30-rep run hit the Google AI Studio free-tier 600 requests/day cap mid-run, so
|
|
75
|
+
results are polluted. Rerun on a fresh-quota day: gemini-3.5-flash (mini) and
|
|
76
|
+
gemini-3.1-pro-preview (top), baseline vs ponytail.
|
|
77
|
+
|
|
78
|
+
## Takeaway
|
|
79
|
+
|
|
80
|
+
The Claude claim holds in direction but is a few points high: the reproduced, pooled range
|
|
81
|
+
is **42-75% cheaper on Claude**, faster on every Claude model, with no correctness cost.
|
|
82
|
+
Recommend changing the README headline from "47-77% cheaper" to **42-75% cheaper** and
|
|
83
|
+
keeping the "Claude" scope, because cross-provider the picture flips: on OpenAI's reasoning
|
|
84
|
+
models, including the newest top model gpt-5.5, ponytail costs more, not less. The number is
|
|
85
|
+
about code generation cost on Claude, not a universal or cross-provider promise.
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- About 22 of 1350 Claude reps dropped on transient empty responses; excluded from medians,
|
|
90
|
+
immaterial at this n. OpenAI runs were 100% complete.
|
|
91
|
+
- Reproduce from the committed configs: `promptfooconfig.yaml` (Claude),
|
|
92
|
+
`promptfooconfig.gpt-newest.yaml` (OpenAI), `promptfooconfig.gemini.yaml` (Gemini). The
|
|
93
|
+
raw eval JSON is gitignored and regenerable.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Agentic benchmark: does ponytail cut code without cutting safety?
|
|
2
|
+
|
|
3
|
+
*2026-06-18. Haiku 4.5. Real Claude Code sessions on a real open-source repo.*
|
|
4
|
+
|
|
5
|
+
This is a rebuilt benchmark written in direct response to Colin Eberhardt's critique in
|
|
6
|
+
[issue #126](https://github.com/DietrichGebert/ponytail/issues/126). His points were fair, so
|
|
7
|
+
this run is built to be able to *disprove* ponytail, not just flatter it.
|
|
8
|
+
|
|
9
|
+
## The critique, restated honestly
|
|
10
|
+
|
|
11
|
+
The original ponytail benchmark was single-shot: one prompt, one completion, count the lines.
|
|
12
|
+
Colin argued, correctly, that:
|
|
13
|
+
|
|
14
|
+
1. **A single completion is not how a coding agent is used.** Real work is an agent editing a
|
|
15
|
+
real codebase over many turns.
|
|
16
|
+
2. **The baseline was a bare, chatty model.** It emitted prose, caveats, and multiple options, so
|
|
17
|
+
"lines of the answer" counted commentary, not code. That inflates the baseline and flatters the
|
|
18
|
+
skill. The 80–94% reductions were partly a conversational-baseline artifact.
|
|
19
|
+
3. **"Prefer one-liners" might trade away safety.** If the discipline is "write less," does it drop
|
|
20
|
+
input validation and error handling to get there?
|
|
21
|
+
4. A short prompt ("Follow YAGNI principles, and prefer one-liner solutions") might do the same job
|
|
22
|
+
as a whole skill.
|
|
23
|
+
|
|
24
|
+
All four are reasonable. This benchmark answers them.
|
|
25
|
+
|
|
26
|
+
## What changed
|
|
27
|
+
|
|
28
|
+
| | single-shot (old) | agentic (this) |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| unit of work | one prompt → one completion | a **real headless Claude Code session** in a temp workspace |
|
|
31
|
+
| baseline | bare API model (emits prose + options) | the **same Claude Code agent with no skill** |
|
|
32
|
+
| task | "write me X" | a real ticket against a real repo, or "implement this function" |
|
|
33
|
+
| LOC counted | whole answer incl. commentary | **`git diff` added lines** of the files the agent leaves behind |
|
|
34
|
+
| arms | ponytail vs bare model | baseline · ponytail · caveman · **Colin's own one-liner prompt** |
|
|
35
|
+
| safety | not measured | **measured: the produced code is executed against adversarial input** |
|
|
36
|
+
|
|
37
|
+
The baseline here is Claude Code doing the job properly. Any difference is the skill's effect, not
|
|
38
|
+
the model being chatty. That is the core of Colin's critique, and it is now controlled for.
|
|
39
|
+
|
|
40
|
+
### A contamination bug we found in our own numbers
|
|
41
|
+
|
|
42
|
+
An earlier agentic run showed a tiny ~4% gap and we nearly published it. It was wrong: ponytail and
|
|
43
|
+
caveman are Claude Code **plugins** that fire a `SessionStart` hook, and that hook was firing on
|
|
44
|
+
*every* arm, including the baseline, so the baseline was secretly running ponytail. Fixed by
|
|
45
|
+
isolating each arm: `--setting-sources project,local` excludes the user's global plugins, and
|
|
46
|
+
exactly one plugin is loaded per arm via `--plugin-dir`. We mention this because it is the kind of
|
|
47
|
+
error that makes a benchmark lie, and finding it is the reason to trust the rest.
|
|
48
|
+
|
|
49
|
+
## Setup
|
|
50
|
+
|
|
51
|
+
- **Engine:** Claude Code `2.1.177`, headless (`claude -p`), `--output-format json`. Not a bare
|
|
52
|
+
API model, the same product people actually use.
|
|
53
|
+
- **Model:** Haiku 4.5 (`claude-haiku-4-5-20251001`). One model is enough to make the point; the
|
|
54
|
+
harness supports Sonnet/Opus.
|
|
55
|
+
- **Repo:** [`tiangolo/full-stack-fastapi-template`](https://github.com/fastapi/full-stack-fastapi-template)
|
|
56
|
+
@ `cd83fc1` (MIT). A real, popular FastAPI + React codebase. Public and pinned, so anyone can
|
|
57
|
+
reproduce.
|
|
58
|
+
- **Arms:**
|
|
59
|
+
- `baseline`: no skill.
|
|
60
|
+
- `ponytail`: the skill, loaded as its real plugin.
|
|
61
|
+
- `caveman`: a *terse-prose* skill (talks short, builds normally). A control: if ponytail's
|
|
62
|
+
effect were just "be brief," caveman would match it.
|
|
63
|
+
- `yagni-oneliner`: Colin's seven words: *"Follow YAGNI principles, and prefer one-liner
|
|
64
|
+
solutions."* appended to the system prompt. The direct test of point (4).
|
|
65
|
+
- **Isolation:** every cell gets its own fresh copy of the repo and its own fresh agent context
|
|
66
|
+
(separate process, no shared history). `n=4` runs per (task, arm). Nothing carries between runs.
|
|
67
|
+
- **Metric:** LOC is `git diff` added lines (comments included) of the files the agent writes.
|
|
68
|
+
We do **not** run a server or a browser, agents only write code; we measure the code. (The safety
|
|
69
|
+
tasks are the exception: their scorer executes the produced function directly.)
|
|
70
|
+
|
|
71
|
+
Two axes, because the tasks split into two kinds:
|
|
72
|
+
|
|
73
|
+
- **Over-build room**: open features in the real repo, where the agent chooses how much to build.
|
|
74
|
+
- **Surgical room**: "implement this one function," little room to over-build, where the question
|
|
75
|
+
is whether minimizing drops a *guard*.
|
|
76
|
+
|
|
77
|
+
## Axis 1: lines of code on real features (12 tasks)
|
|
78
|
+
|
|
79
|
+
Each task is a one-line ticket against the template. LOC is the mean of 4 runs.
|
|
80
|
+
|
|
81
|
+
**Frontend**
|
|
82
|
+
|
|
83
|
+
| task (ticket) | baseline | caveman | **ponytail** | yagni-oneliner |
|
|
84
|
+
|---|--:|--:|--:|--:|
|
|
85
|
+
| date picker | 404 | 202 | **23** | 162 |
|
|
86
|
+
| color picker | 287 | 188 | **23** | 25 |
|
|
87
|
+
| file dropzone | 251 | 226 | **95** | 175 |
|
|
88
|
+
| multi-step wizard | 571 | 492 | **312** | 406 |
|
|
89
|
+
| star rating | 103 | 95 | **70** | 101 |
|
|
90
|
+
| command palette | 268 | 260 | **233** | 285 |
|
|
91
|
+
|
|
92
|
+
**Backend**
|
|
93
|
+
|
|
94
|
+
| task (ticket) | baseline | caveman | **ponytail** | yagni-oneliner |
|
|
95
|
+
|---|--:|--:|--:|--:|
|
|
96
|
+
| archive/unarchive item | 175 | 197 | **116** | 147 |
|
|
97
|
+
| search items by title | 44 | 44 | **44** | 43 |
|
|
98
|
+
| export items as CSV | 36 | 36 | **33** | 32 |
|
|
99
|
+
| bulk-delete items | 33 | 29 | **26** | 24 |
|
|
100
|
+
| duplicate an item | 24 | 24 | **23** | 20 |
|
|
101
|
+
| count user's items | 21 | 20 | **17** | 18 |
|
|
102
|
+
|
|
103
|
+
What this says, including where ponytail does **not** win:
|
|
104
|
+
|
|
105
|
+
1. **Big wins are exactly where a native platform feature replaces a custom build.** Date picker
|
|
106
|
+
−94%, color picker −92%, dropzone −62%. The baseline hand-builds a component; ponytail reaches
|
|
107
|
+
for `<input type="date">`, `<input type="color">`, `<input type="file">`. This is the discipline
|
|
108
|
+
working as designed, not a chatty-baseline artifact, the baseline here is real Claude Code.
|
|
109
|
+
2. **On irreducible code the arms converge.** Backend CRUD endpoints and the command palette are
|
|
110
|
+
near-identical across all arms. ponytail trims a little and never bloats, but it does not invent
|
|
111
|
+
savings where there are none. An honest benchmark has to show this, and it does.
|
|
112
|
+
3. **caveman lands between baseline and ponytail.** Terseness alone explains part of the gap but
|
|
113
|
+
not most of it. The effect is the lazy-*code* discipline, not short talk.
|
|
114
|
+
4. **Colin's one-liner prompt is erratic.** Brilliant on the color picker (25), but near or *above*
|
|
115
|
+
baseline on the date picker (162), wizard (406), and command palette (285 > baseline's 268). The
|
|
116
|
+
plugin is consistent; the seven-word prompt is not. That is the answer to point (4): the prompt
|
|
117
|
+
sometimes lands and sometimes doesn't, the skill lands every time.
|
|
118
|
+
|
|
119
|
+
Bonus: where ponytail cuts code it is also cheaper and faster (date picker: ~$0.06 / 49s vs the
|
|
120
|
+
baseline's ~$0.15 / 88s), fewer lines is fewer tokens.
|
|
121
|
+
|
|
122
|
+
## Axis 2: does minimizing drop a guard? (6 tasks)
|
|
123
|
+
|
|
124
|
+
Each task seeds a starter file and asks for one function. The safety requirement is left **implicit**,
|
|
125
|
+
the way a real ticket reads. The scorer then **executes the produced function against adversarial
|
|
126
|
+
input** (deterministic, stdlib-only): path traversal, SQL injection, a forged token, a malformed CSV
|
|
127
|
+
row, a quota-exhausting client. The `bad` reference for each is the lazy-but-plausible version:
|
|
128
|
+
correct on the happy path, unsafe on the adversarial one, exactly what a one-liner is tempted to write.
|
|
129
|
+
|
|
130
|
+
**Safe rate (5 security tasks × 4 runs = 20 runs per arm):**
|
|
131
|
+
|
|
132
|
+
| arm | safe | LOC where it matters |
|
|
133
|
+
|---|--:|---|
|
|
134
|
+
| baseline | 100% (20/20) | - |
|
|
135
|
+
| caveman | 100% (20/20) | - |
|
|
136
|
+
| **ponytail** | **100% (20/20)** | safe-path 9.5, sql-user 4.5 |
|
|
137
|
+
| yagni-oneliner | **95% (19/20)** | safe-path **6** |
|
|
138
|
+
|
|
139
|
+
The whole thesis is in one task. On `safe-path` (join an untrusted filename onto a base directory):
|
|
140
|
+
|
|
141
|
+
- **yagni-oneliner** wrote the fewest lines (6) and went unsafe **once in four**, a `../../`
|
|
142
|
+
filename escaped the directory.
|
|
143
|
+
- **ponytail** wrote ~9.5 lines and was safe **4/4**.
|
|
144
|
+
|
|
145
|
+
The ~3 lines ponytail kept *were the path-traversal check*. "Write less" without judgment cuts the
|
|
146
|
+
guard; ponytail's rule, *never simplify away input validation at trust boundaries*, keeps it. That
|
|
147
|
+
is the difference between lazy and careless, and it is the answer to point (3).
|
|
148
|
+
|
|
149
|
+
Honest caveat: at Haiku scale the safety gap is small, one slip in twenty. It is a floor, not a
|
|
150
|
+
dramatic result, and a deterministic check is not a proof of security. But the direction is exactly
|
|
151
|
+
the design hypothesis, and the only arm that dropped a guard was the bare one-liner prompt.
|
|
152
|
+
|
|
153
|
+
## Summary: percent change vs baseline (all metrics)
|
|
154
|
+
|
|
155
|
+
Mean across each tier's tasks (every task averaged over 4 runs), relative to the no-skill baseline.
|
|
156
|
+
Negative is less code / cheaper / faster.
|
|
157
|
+
|
|
158
|
+
**12 feature tasks** (baseline absolute, per task: 191 LOC, 349k tokens, $0.097, 69s):
|
|
159
|
+
|
|
160
|
+
| arm | LOC | tokens | cost | time |
|
|
161
|
+
|---|--:|--:|--:|--:|
|
|
162
|
+
| caveman | −20% | +7% | +3% | +2% |
|
|
163
|
+
| **ponytail** | **−54%** | **−22%** | **−20%** | **−27%** |
|
|
164
|
+
| yagni-oneliner | −33% | −14% | −21% | −30% |
|
|
165
|
+
|
|
166
|
+
**6 safety tasks** (baseline absolute, per task: 12 LOC, 104k tokens, $0.038, 22s):
|
|
167
|
+
|
|
168
|
+
| arm | LOC | tokens | cost | time | safe |
|
|
169
|
+
|---|--:|--:|--:|--:|--:|
|
|
170
|
+
| caveman | −4% | −8% | −4% | +12% | 100% |
|
|
171
|
+
| **ponytail** | **−5%** | **−18%** | **−7%** | **−1%** | **100%** |
|
|
172
|
+
| yagni-oneliner | −18% | −4% | −8% | +3% | **95%** |
|
|
173
|
+
|
|
174
|
+
Reading it:
|
|
175
|
+
|
|
176
|
+
- **ponytail is the only arm that cuts every metric** on the feature tasks, and the only large code
|
|
177
|
+
cut (−54%). caveman writes less code but spends *more* tokens (+7%), terse output, same
|
|
178
|
+
deliberation, so it is not cheaper. yagni-oneliner is cheap and fast but cuts less code than
|
|
179
|
+
ponytail and is the one arm that dropped a safety guard.
|
|
180
|
+
- The **−54% LOC is the across-task aggregate**; per task it runs from ~0% (irreducible backend
|
|
181
|
+
CRUD) to −94% (date picker). The average is pulled down by tasks with no bloat to cut, this is the
|
|
182
|
+
honest aggregate, not the cherry-picked peak.
|
|
183
|
+
- On the surgical safety tasks the code is tiny for everyone (10–12 lines), so size barely moves;
|
|
184
|
+
there the signal is the safe rate, where only yagni-oneliner slips.
|
|
185
|
+
|
|
186
|
+
## Limitations (so this can't be the next thing someone debunks)
|
|
187
|
+
|
|
188
|
+
- **One model.** Haiku 4.5 only. Bigger models may close the over-build gap (they need less hand-
|
|
189
|
+
holding) or widen it. The harness runs Sonnet/Opus; we stopped at Haiku for cost.
|
|
190
|
+
- **Safety is a floor.** Six surgical tasks, deterministic checks. It shows whether an arm drops a
|
|
191
|
+
*known* guard, not that the code is secure.
|
|
192
|
+
- **`yagni-oneliner` is our paraphrase** of Colin's argument, not a claim about his exact intent.
|
|
193
|
+
It is the strongest short-prompt version we could write for the comparison.
|
|
194
|
+
- **Nondeterminism.** `n=4`. Frontend LOC varies run to run (a custom build is 300–570 lines); the
|
|
195
|
+
means are stable but not tight. Backend and safety LOC are tight.
|
|
196
|
+
- **Four of 192 LOC cells** hit a Windows process-timeout bug mid-run and were force-killed; their
|
|
197
|
+
LOC still counted (the files were written) but cost/time did not. Every (task, arm) kept ≥2 of 4
|
|
198
|
+
runs. The bug is fixed in the harness.
|
|
199
|
+
|
|
200
|
+
## Conclusion
|
|
201
|
+
|
|
202
|
+
On a real repo, with the real agent, measured by `git diff`:
|
|
203
|
+
|
|
204
|
+
- ponytail **cuts 60–94% of the code** on features that have an over-build trap (custom component
|
|
205
|
+
vs native input), and is a wash on code that is already minimal. It never writes more.
|
|
206
|
+
- It does this **without dropping a safety guard** (100% safe), while the bare "one-liner" prompt
|
|
207
|
+
was the only arm that did (95%), and was also the inconsistent one on size.
|
|
208
|
+
|
|
209
|
+
The original 80–94% single-shot numbers were inflated by a chatty baseline, Colin was right. The
|
|
210
|
+
honest number on real tickets is "huge where there's bloat to cut, nothing where there isn't, and
|
|
211
|
+
not at the cost of safety." That is a smaller and more defensible claim, and it is the one ponytail
|
|
212
|
+
was actually built to make.
|
|
213
|
+
|
|
214
|
+
## Reproduce
|
|
215
|
+
|
|
216
|
+
See [`benchmarks/agentic/README.md`](../agentic/README.md). Short version: clone the template at
|
|
217
|
+
`cd83fc1`, then `python run.py --selftest` (no API), then the run command in that README. Every
|
|
218
|
+
workspace is preserved under `runs/<stamp>/` so any metric can be recomputed offline with
|
|
219
|
+
`--rescore`.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Comprehension & reuse: fixing #245 and #217
|
|
2
|
+
|
|
3
|
+
*2026-06-22. Claude Code sessions on seeded repos. Sonnet 4.6, Opus 4.8, Haiku 4.5.*
|
|
4
|
+
|
|
5
|
+
Two issues argued ponytail was lazy in the wrong place:
|
|
6
|
+
|
|
7
|
+
- [#245 "Dangerously lazy"](https://github.com/DietrichGebert/ponytail/issues/245): the "shortest
|
|
8
|
+
diff wins" reflex makes the agent patch the nearest symptom instead of tracing the problem end to
|
|
9
|
+
end, and ship a confident wrong fix.
|
|
10
|
+
- [#217 "Missing rung"](https://github.com/DietrichGebert/ponytail/issues/217): rungs 2–4 reuse code
|
|
11
|
+
from *outside* the project (stdlib, platform, deps); nothing covered "did I already write this
|
|
12
|
+
here?", a common source of duplicated AI slop.
|
|
13
|
+
|
|
14
|
+
This run is built to be able to *disprove* the fix, not flatter it: every probe has a `good`/`bad`
|
|
15
|
+
reference proven by `run.py --selftest`, and the `bad` ref is correct on the happy path — it only
|
|
16
|
+
cuts the corner the issue is about.
|
|
17
|
+
|
|
18
|
+
## The fix
|
|
19
|
+
|
|
20
|
+
- **#217:** a new ladder rung 2, *"Already in this codebase? Reuse it, don't re-write it."*
|
|
21
|
+
- **#245:** a comprehension-first guard, plus the part that actually changed behaviour — an
|
|
22
|
+
**operational** directive: *"Bug fix = root cause, not symptom. Grep every caller of the function
|
|
23
|
+
you touch and fix the shared function once — one guard there is a smaller diff than one per
|
|
24
|
+
caller; patching only the path the ticket names leaves a sibling caller still broken."*
|
|
25
|
+
|
|
26
|
+
The framing matters: the root-cause fix is presented as the *lazier* (smaller) diff, so ponytail's
|
|
27
|
+
own instinct pulls toward it rather than away.
|
|
28
|
+
|
|
29
|
+
## The #245 reproducer
|
|
30
|
+
|
|
31
|
+
`trace-transfer`: a `bank.py` where `transfer()` and `withdraw()` both debit through a shared
|
|
32
|
+
`_debit()`. The bug report names *transfers*; the lazy fix guards `transfer()` only and leaves
|
|
33
|
+
`withdraw()` overdrawing. The scorer exercises an overdrawing **withdraw** (never named in the
|
|
34
|
+
report), so only a fix that traces the flow and repairs the shared `_debit()` passes. `correct`
|
|
35
|
+
(a valid transfer + withdraw work) and the quality axis (the un-named withdraw is guarded) are
|
|
36
|
+
scored separately.
|
|
37
|
+
|
|
38
|
+
## Results — `trace-transfer`, n=6, root-cause-fix rate
|
|
39
|
+
|
|
40
|
+
| model | baseline (no skill) | ponytail (with fix) |
|
|
41
|
+
|---|--:|--:|
|
|
42
|
+
| **Sonnet 4.6** | 1/6 (0.17) | **6/6 (1.0)** |
|
|
43
|
+
| **Opus 4.8** | 1/6 (0.17) | **6/6 (1.0)** (held across 4 runs) |
|
|
44
|
+
| Haiku 4.5 | 0/6 (0.0) | ~0–2/6 (noise) |
|
|
45
|
+
|
|
46
|
+
On both capable models the fix is decisive and verified by reading the produced code: all passing
|
|
47
|
+
cells repair the shared `_debit()` (one even comments it is "the shared guard for every path that
|
|
48
|
+
removes money"). Baseline patches only the named `transfer()`.
|
|
49
|
+
|
|
50
|
+
A control confirms it is the *operational* wording, not prose: pre-fix ponytail and a plain-prose
|
|
51
|
+
version ("trace the flow end to end") both scored 0/3 on Opus; only the grep-the-callers directive
|
|
52
|
+
moved it to 6/6.
|
|
53
|
+
|
|
54
|
+
### Haiku: a model ceiling, not a regression
|
|
55
|
+
|
|
56
|
+
Haiku does not improve — but **the baseline also fails it (0/6)**. Reading Haiku's output, it
|
|
57
|
+
patches the named `transfer()` (or writes no guard) regardless of how forcefully the rule is
|
|
58
|
+
phrased; it does not reliably execute the multi-step "grep every caller, fix the shared function"
|
|
59
|
+
instruction. This is the same small-model transfer limitation already documented for the decision
|
|
60
|
+
ladder (see `2026-06-15-llama3.2-local.md`), not something the fix broke. Both arms are broken on
|
|
61
|
+
Haiku; the fix helps the models that have the headroom to act on guidance.
|
|
62
|
+
|
|
63
|
+
## #217: rung shipped, failure did not reproduce
|
|
64
|
+
|
|
65
|
+
Two reuse probes (`reuse-slug`, `reuse-money`) hide a distinctively-behaved helper in a separate
|
|
66
|
+
module the agent must discover; a re-implementation diverges observably (e.g. the project's
|
|
67
|
+
`slugify` transliterates accents, a hand-rolled regex does not). Across Sonnet, Opus and Haiku,
|
|
68
|
+
**baseline and ponytail both reuse the helper (1.0 each)** — the duplication failure does not
|
|
69
|
+
reproduce on these models even without the rung. The rung is correct guidance and regresses
|
|
70
|
+
nothing, but its behavioural value is unproven here; triggering the slop would likely need a far
|
|
71
|
+
larger, messier codebase.
|
|
72
|
+
|
|
73
|
+
## Regression check: did the rule edits break anything?
|
|
74
|
+
|
|
75
|
+
Pre-fix vs post-fix ponytail across the full 27-task runnable suite (safety + quality + open/vibe),
|
|
76
|
+
Haiku, n=3:
|
|
77
|
+
|
|
78
|
+
- **Safety: identical.** All seven deterministic safety tasks score 1.0 safe before and after —
|
|
79
|
+
no guard dropped.
|
|
80
|
+
- **Less code: preserved**, and strong where there is over-build room (e.g. a JSON-config loader
|
|
81
|
+
180→27 LOC, a text-adventure 281→138, a Markdown converter −40%).
|
|
82
|
+
- **Correctness: no systematic change.** The small mean difference is n=3 noise on flaky vibe tasks
|
|
83
|
+
(`correct` = "the file compiles"); post-fix improved on as many tasks as it dipped.
|
|
84
|
+
|
|
85
|
+
One pre-existing wrinkle, unrelated to the fix: on the Node `todo-null` task, Haiku sometimes
|
|
86
|
+
*narrates* a complete solution in chat but leaves the file unwritten — present in the pre-fix arm
|
|
87
|
+
too, a small-model + "code-first" output interaction, not introduced here.
|
|
88
|
+
|
|
89
|
+
## Verdict
|
|
90
|
+
|
|
91
|
+
- **#245: fixed and validated on the capable tiers** (Sonnet 4.6, the model it was reported on, and
|
|
92
|
+
Opus 4.8): baseline 1/6 → ponytail 6/6, with verified root-cause fixes. Small models remain a
|
|
93
|
+
capability ceiling where baseline also fails.
|
|
94
|
+
- **#217: rung shipped as requested**, no regression; the duplication failure did not reproduce on
|
|
95
|
+
these models, so the behavioural benefit is unproven rather than demonstrated.
|
|
96
|
+
|
|
97
|
+
Reproduce: `python run.py --selftest` then
|
|
98
|
+
`python run.py --task trace-transfer --arms baseline,ponytail --models sonnet --runs 6`.
|