chill-agent 1.5.0 → 1.6.1

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.
Files changed (188) hide show
  1. package/.claude-plugin/contrast-mcp-launcher.mjs +31 -0
  2. package/.claude-plugin/plugin.json +8 -3
  3. package/.cline-plugin/plugin.js +6 -0
  4. package/.codex-plugin/plugin.json +4 -3
  5. package/.cursor-plugin/marketplace.json +16 -0
  6. package/.cursor-plugin/plugin.json +2 -2
  7. package/.devin-plugin/plugin.json +2 -2
  8. package/.kimi-plugin/plugin.json +11 -29
  9. package/.omp-plugin/marketplace.json +15 -0
  10. package/.openclaw/skills/ponytail/SKILL.md +2 -7
  11. package/.openclaw/skills/ponytail-audit/SKILL.md +3 -6
  12. package/.openclaw/skills/ponytail-debt/SKILL.md +3 -4
  13. package/.openclaw/skills/ponytail-gain/SKILL.md +3 -4
  14. package/.openclaw/skills/ponytail-help/SKILL.md +3 -3
  15. package/.openclaw/skills/ponytail-review/SKILL.md +3 -6
  16. package/.opencode/plugins/ponytail.mjs +1 -1
  17. package/.qoder-plugin/plugin.json +12 -26
  18. package/AGENTS.md +32 -19
  19. package/__init__.py +1 -1
  20. package/assets/antislop-banner.png +0 -0
  21. package/assets/band/band.png +0 -0
  22. package/assets/band/board.png +0 -0
  23. package/assets/benchmark-3model.svg +21 -0
  24. package/assets/benchmark-agentic.svg +62 -0
  25. package/assets/codex-icon.png +0 -0
  26. package/assets/compare/code/after.webp +0 -0
  27. package/assets/compare/code/before.webp +0 -0
  28. package/assets/compare/text/after.webp +0 -0
  29. package/assets/compare/text/before.webp +0 -0
  30. package/assets/compare/ui/w-all.webp +0 -0
  31. package/assets/compare/ui/w-antislop.webp +0 -0
  32. package/assets/compare/ui/w-design.webp +0 -0
  33. package/assets/compare/ui/without.webp +0 -0
  34. package/assets/logo-dark.png +0 -0
  35. package/assets/logo-dark.svg +115 -0
  36. package/assets/logo-greenpt-dark.svg +27 -0
  37. package/assets/logo-greenpt.svg +27 -0
  38. package/assets/logo.png +0 -0
  39. package/assets/photoreunion-logo-dark.svg +4 -0
  40. package/assets/photoreunion-logo-light.svg +4 -0
  41. package/assets/profile.png +0 -0
  42. package/assets/retriever-logo-dark.svg +4 -0
  43. package/assets/retriever-logo-light.svg +4 -0
  44. package/assets/stories-banner.png +0 -0
  45. package/assets/v5/audit-es.png +0 -0
  46. package/assets/v5/audit-ja.png +0 -0
  47. package/assets/v5/audit-ko.png +0 -0
  48. package/assets/v5/audit-zh-CN.png +0 -0
  49. package/assets/v5/audit.png +0 -0
  50. package/assets/v5/beforeafter-es.png +0 -0
  51. package/assets/v5/beforeafter-ja.png +0 -0
  52. package/assets/v5/beforeafter-ko.png +0 -0
  53. package/assets/v5/beforeafter-zh-CN.png +0 -0
  54. package/assets/v5/beforeafter.png +0 -0
  55. package/assets/v5/chart-es.png +0 -0
  56. package/assets/v5/chart-ja.png +0 -0
  57. package/assets/v5/chart-ko.png +0 -0
  58. package/assets/v5/chart-zh-CN.png +0 -0
  59. package/assets/v5/chart.png +0 -0
  60. package/assets/v5/hero-es.jpg +0 -0
  61. package/assets/v5/hero-ja.jpg +0 -0
  62. package/assets/v5/hero-ko.jpg +0 -0
  63. package/assets/v5/hero-zh-CN.jpg +0 -0
  64. package/assets/v5/hero.jpg +0 -0
  65. package/assets/v5/ladder-es.png +0 -0
  66. package/assets/v5/ladder-ja.png +0 -0
  67. package/assets/v5/ladder-ko.png +0 -0
  68. package/assets/v5/ladder-zh-CN.png +0 -0
  69. package/assets/v5/ladder.png +0 -0
  70. package/assets/v5/review-es.png +0 -0
  71. package/assets/v5/review-ja.png +0 -0
  72. package/assets/v5/review-ko.png +0 -0
  73. package/assets/v5/review-zh-CN.png +0 -0
  74. package/assets/v5/review.png +0 -0
  75. package/assets/v5/tests-es.png +0 -0
  76. package/assets/v5/tests-ja.png +0 -0
  77. package/assets/v5/tests-ko.png +0 -0
  78. package/assets/v5/tests-zh-CN.png +0 -0
  79. package/assets/v5/tests.png +0 -0
  80. package/assets/waitlist-banner-es.png +0 -0
  81. package/assets/waitlist-banner-ja.png +0 -0
  82. package/assets/waitlist-banner-ko.png +0 -0
  83. package/assets/waitlist-banner-zh-CN.png +0 -0
  84. package/assets/waitlist-banner.png +0 -0
  85. package/benchmarks/.env.example +2 -0
  86. package/benchmarks/agentic/README.md +201 -0
  87. package/benchmarks/agentic/answer.py +288 -0
  88. package/benchmarks/agentic/compare.py +93 -0
  89. package/benchmarks/agentic/complete.py +178 -0
  90. package/benchmarks/agentic/judge.py +198 -0
  91. package/benchmarks/agentic/run.py +543 -0
  92. package/benchmarks/agentic/tasks.py +1544 -0
  93. package/benchmarks/arms/baseline.js +2 -0
  94. package/benchmarks/arms/caveman-SKILL.md +67 -0
  95. package/benchmarks/arms/caveman.js +8 -0
  96. package/benchmarks/arms/ponytail.js +8 -0
  97. package/benchmarks/behavior.js +54 -0
  98. package/benchmarks/behavior.yaml +40 -0
  99. package/benchmarks/correctness.js +291 -0
  100. package/benchmarks/loc.js +17 -19
  101. package/benchmarks/promptfooconfig.gemini.yaml +32 -0
  102. package/benchmarks/promptfooconfig.gpt-newest.yaml +33 -0
  103. package/benchmarks/promptfooconfig.yaml +41 -0
  104. package/benchmarks/results/2026-06-12-caveman-vs-ponytail.md +71 -0
  105. package/benchmarks/results/2026-06-12-v4-hardening-vs-caveman.md +125 -0
  106. package/benchmarks/results/2026-06-15-llama3.2-local.md +82 -0
  107. package/benchmarks/results/2026-06-16-correctness-gate-fix.md +113 -0
  108. package/benchmarks/results/2026-06-16-robustness-audit.md +135 -0
  109. package/benchmarks/results/2026-06-17-agentic-safety.md +164 -0
  110. package/benchmarks/results/2026-06-17-cost-verification.md +93 -0
  111. package/benchmarks/results/2026-06-18-agentic.md +219 -0
  112. package/benchmarks/results/2026-06-22-issue-245-217-comprehension.md +98 -0
  113. package/benchmarks/results/2026-10-07-agentic.md +186 -0
  114. package/bin/chill-agent.js +6 -6
  115. package/bin/chill-agent.mjs +7 -0
  116. package/cli/index.mjs +241 -0
  117. package/cli/lib/banner.mjs +21 -0
  118. package/cli/lib/install.mjs +498 -0
  119. package/cli/lib/settings.mjs +48 -0
  120. package/cli/package-lock.json +87 -0
  121. package/cli/package.json +42 -0
  122. package/cli/scripts/smoke-test.mjs +95 -0
  123. package/cli/scripts/smoke-worker.mjs +411 -0
  124. package/cli/scripts/sync-skills.mjs +81 -0
  125. package/examples/email-validation.md +156 -0
  126. package/examples/group-by.md +35 -0
  127. package/examples/infinite-scroll.md +58 -0
  128. package/examples/number-formatting.md +37 -0
  129. package/examples/rate-limit.md +272 -0
  130. package/examples/react-countdown.md +390 -0
  131. package/examples/url-params.md +41 -0
  132. package/gemini-extension.json +2 -2
  133. package/hooks/band/board.ts +560 -0
  134. package/hooks/band/logo.ts +3 -0
  135. package/hooks/band/metrics.ts +311 -0
  136. package/hooks/band/register.tsx +495 -0
  137. package/hooks/band/types.d.ts +34 -0
  138. package/hooks/claude-band.json +6 -0
  139. package/hooks/claude-codex-hooks.json +34 -4
  140. package/hooks/copilot-hooks.json +12 -2
  141. package/hooks/cursor-hooks.json +2 -2
  142. package/hooks/ponytail-band-import.js +274 -0
  143. package/hooks/ponytail-config.js +175 -0
  144. package/hooks/ponytail-map.js +111 -0
  145. package/hooks/ponytail-subagent.js +94 -0
  146. package/hooks/qoder-hooks.json +19 -4
  147. package/i18n/README.ko.md +230 -0
  148. package/package.json +12 -7
  149. package/pi-extension/test/extension.test.js +367 -0
  150. package/pi-extension/test/helpers.test.js +179 -0
  151. package/plugin.yaml +21 -12
  152. package/rules/antislop.mdc +18 -0
  153. package/scripts/build-openclaw-skills.js +60 -0
  154. package/scripts/check-repo.mjs +389 -0
  155. package/scripts/publish-openclaw-skills.js +75 -0
  156. package/scripts/skill-index.mjs +81 -0
  157. package/skills/antislop/SKILL.md +700 -0
  158. package/skills/antislop/VERSION +1 -0
  159. package/skills/antislop-code/SKILL.md +128 -0
  160. package/skills/antislop-copywriting/SKILL.md +407 -0
  161. package/skills/antislop-human/LICENSE-web-quality-skills +21 -0
  162. package/skills/antislop-human/SKILL.md +204 -0
  163. package/skills/antislop-human/contrast-check.py +168 -0
  164. package/skills/antislop-human/contrast-mcp.py +121 -0
  165. package/skills/antislop-human/references/A11Y-PATTERNS.md +249 -0
  166. package/skills/antislop-human/references/WCAG.md +127 -0
  167. package/skills/antislop-layoutmobile/SKILL.md +170 -0
  168. package/skills/antislop-ui/LICENSE-web-quality-skills +21 -0
  169. package/skills/antislop-ui/SKILL.md +344 -0
  170. package/skills/antislop-ui/references/CORE-WEB-VITALS.md +181 -0
  171. package/skills/antislop-ui/references/WEB-BASELINE.md +28 -0
  172. package/skills/plugin.json +5 -0
  173. package/src/package.json +3 -0
  174. package/tests/activation-map.test.js +63 -0
  175. package/tests/band-import.test.js +111 -0
  176. package/tests/band.test.tsx +520 -0
  177. package/tests/behavior.test.js +80 -0
  178. package/tests/gemini-extension.test.js +2 -2
  179. package/tests/grok-plugin.test.js +1 -1
  180. package/tests/hermes-plugin.test.js +1 -1
  181. package/tests/hooks-windows.test.js +205 -0
  182. package/tests/kimi-plugin.test.js +1 -1
  183. package/tests/map.test.js +30 -0
  184. package/tests/package-scripts.test.js +35 -0
  185. package/tests/package.test.js +23 -0
  186. package/tests/uninstall.test.js +202 -0
  187. /package/tests/{manifests.test.js → manifests.test.mjs} +0 -0
  188. /package/tests/{rules.test.js → rules.test.mjs} +0 -0
@@ -0,0 +1,71 @@
1
+ # Caveman vs Ponytail — 2026-06-12
2
+
3
+ 5 coding tasks × fresh subagent per config, same model. Tokens = agent total
4
+ (includes thinking). Code lines = fenced blocks in the deliverable, approx.
5
+ n=1 per cell — durations carry noise.
6
+
7
+ ## Ponytail v1 (before this benchmark)
8
+
9
+ | Task | Baseline | Caveman | Ponytail v1 |
10
+ |---|---|---|---|
11
+ | email | 31,971 tok · 104s · ~34 loc | 26,464 · 20s · ~25 | 27,582 · 32s · 6 |
12
+ | debounce | 23,966 · 25s · ~38 | 26,496 · 19s · ~36 | 27,812 · 33s · 6 |
13
+ | csv-sum | 22,607 · 11s · 6 | 26,062 · 13s · 6 | 26,867 · 20s · 7 |
14
+ | react-countdown | 44,629 · 208s · ~190 | 26,656 · 21s · ~30 | 28,748 · 49s · 13 |
15
+ | rate-limit | 38,782 · 131s · ~25 | 32,732 · 63s · ~20 | 32,579 · 93s · ~21 |
16
+ | **Total** | **161,955 · 479s · ~293** | **138,410 · 136s · ~117** | **143,588 · 228s · ~53** |
17
+
18
+ ### v1 findings
19
+
20
+ 1. Code minimalism: ponytail dominated (2.2× fewer lines than caveman, 5.5× fewer than baseline).
21
+ 2. Total tokens: caveman won by ~4% — ponytail wrote minimal code, then long
22
+ "skipped on purpose" essays. Prose ate the code savings.
23
+ 3. Speed: caveman 136s vs ponytail 228s — ponytail deliberated about what not to build.
24
+ 4. Floor effect: on already-minimal tasks (csv-sum) both skills pay ~3k tokens
25
+ skill-read tax over baseline.
26
+
27
+ ## Ponytail v2 (after fixes)
28
+
29
+ v2 changes: Output cap (code + ≤3 short lines, "if the explanation is longer
30
+ than the code, delete the explanation"), ladder-is-a-reflex clause
31
+ (anti-deliberation), ship-and-question rule (never stall on "do you need X?").
32
+
33
+ | Task | Ponytail v2 | Δ vs v1 | Δ vs caveman |
34
+ |---|---|---|---|
35
+ | email | 26,705 · 21s · 5 loc | −877 tok · −11s | +241 tok · +1s |
36
+ | debounce | 27,185 · 25s · 6 | −627 · −8s | +689 · +6s |
37
+ | csv-sum | 26,278 · 15s · 6 | −589 · −5s | +216 · +2s |
38
+ | react-countdown | 27,598 · 29s · 13 | −1,150 · −20s | +942 · +8s |
39
+ | rate-limit | 28,858 · 48s · 17 | −3,721 · −45s | −3,874 · −15s |
40
+ | **Total** | **136,624 · 158s · 47** | **−6,964 (−4.8%) · −70s (−31%)** | **−1,786 (−1.3%) · +22s** |
41
+
42
+ ## Ponytail v3 (skill file compressed)
43
+
44
+ v3 change: SKILL.md 115 → 95 lines, same substance — the minimalism skill
45
+ should not be 2× caveman's length. Cuts read cost per invocation and
46
+ injection cost per session.
47
+
48
+ | Task | Ponytail v3 | Δ vs v2 | Δ vs caveman |
49
+ |---|---|---|---|
50
+ | email | 26,573 · 19s · 5 loc | −132 · −2s | +109 · −1s |
51
+ | debounce | 26,745 · 22s · 5 | −440 · −3s | +249 · +3s |
52
+ | csv-sum | 26,251 · 15s · 6 | −27 · 0s | +189 · +2s |
53
+ | react-countdown | 26,961 · 22s · 13 | −637 · −7s | +305 · +1s |
54
+ | rate-limit | 29,179 · 49s · 18 | +321 · +1s | −3,553 · −14s |
55
+ | **Total** | **135,709 · 127s · 47** | **−915 · −31s (−20%)** | **−2,701 (−2.0%) · −9s (−7%)** |
56
+
57
+ ## Verdict (v3)
58
+
59
+ | Area | Winner |
60
+ |---|---|
61
+ | Code size | **Ponytail** — 47 vs 117 lines (2.5×) |
62
+ | Deliverable prose | **Ponytail** — capped at 3 skip-lines, under caveman's gotcha lists |
63
+ | Total tokens (cost) | **Ponytail** — 135.7k vs 138.4k (−2.0%) |
64
+ | Wall time | **Ponytail** — 127s vs 136s (−7%; n=1, treat as parity-or-better) |
65
+ | Follow-up prevention | **Ponytail** — every skip names its escalation trigger |
66
+
67
+ Both skills demolish the no-skill baseline: −16% tokens, ~3× faster, and the
68
+ baseline's degenerate cases (190-line countdown dashboard, 208s) simply don't
69
+ happen. Remaining ~3.6k floor tax vs baseline on trivial tasks is mostly the
70
+ benchmark's explicit SKILL.md read — production sessions get rules injected
71
+ by the SessionStart hook and don't pay it.
@@ -0,0 +1,125 @@
1
+ # Ponytail v4 hardening — A–F benchmark vs Caveman (2026-06-12)
2
+
3
+ Response to the hardening brief in `C:\dev\ponytail-bench\PONYTAIL-BENCHMARK-WRITEUP.md`.
4
+ Harness reused as-is: same 6 tasks (specs reconstructed in `ponytail-bench\specs.md` —
5
+ the originals were not preserved; both new arms got identical text), same scorer
6
+ (`score.py`, arms now auto-discovered), same adversarial probes (`probe_e.py`,
7
+ `probe_f.py`), same extension protocol (phase-1 git commit, cost = `git diff
8
+ --numstat` insertions + new-file LOC). Caveman = `JuliusBrussee/caveman` SKILL.md
9
+ verbatim (full level), saved at `ponytail-bench\caveman-SKILL.md`. One fresh
10
+ subagent per task × arm, same model for all 16 runs. Caveat: this model/harness
11
+ differs from the original Cursor runs, so comparisons to the old treatment
12
+ numbers are directional; the ponytail4-vs-caveman head-to-head is same-model.
13
+
14
+ ## v4 changes (the hardening, ~10 lines of prompt total)
15
+
16
+ 1. **Test reflex** (brief 5.1): non-trivial logic leaves ONE runnable check —
17
+ assert-based `demo()`/`__main__` self-check or one small `test_*.py`. No
18
+ frameworks. One-liners need no test.
19
+ 2. **Ceiling comments** (5.2): a `ponytail:` shortcut with a known ceiling must
20
+ name the ceiling and the upgrade path in the comment.
21
+ 3. **Robust variant rule** (5.3): between two same-size stdlib options, take the
22
+ edge-case-correct one.
23
+
24
+ Applied to SKILL.md, all five cross-agent rule copies, the hook fallback, and a
25
+ guard line in ponytail-review (never flag the minimal check as bloat).
26
+
27
+ ## Build phase — non-blank LOC / .py files (scorer-verified)
28
+
29
+ | Task | Control (orig) | Treatment v3 (orig) | **Ponytail v4** | **Caveman** |
30
+ |---|--:|--:|--:|--:|
31
+ | A log CLI | 970 / 13 | 150 / 1 | **145 / 2** | 283 / 1 |
32
+ | B file sync | 587 / 9 | 175 / 2 | **99 / 1** | 228 / 2 |
33
+ | C dispatcher | 726 / 13 | 85 / 1 | **73 / 1** | 396 / 10 |
34
+ | D validation | 343 / 8 | 93 / 1 | **70 / 1** | 218 / 3 |
35
+ | E auth | 155 / 1 | 74 / 1 | **49 / 1** | 148 / 1 |
36
+ | F ledger | 162 / 1 | 86 / 1 | **54 / 1** | 167 / 1 |
37
+ | **Total** | 2943 | 663 | **490** | 1440 |
38
+
39
+ v4 is at or below v3 on every task (−3% to −43%) **despite now shipping a
40
+ runnable check in all six arms** — the test reflex did not cause bloat creep.
41
+ v4 is 34% of Caveman's size. Task A is the one place Caveman has fewer .py
42
+ files (1 vs 2): v4's second file is the 24-line regression check Caveman
43
+ doesn't ship — deleting it to win file count would sacrifice the safety clause
44
+ to win on size, which the brief forbids.
45
+
46
+ ## Extension phase (tasks C, D — surprise requests, git-measured)
47
+
48
+ | Metric | C v4 | C caveman | D v4 | D caveman |
49
+ |---|--:|--:|--:|--:|
50
+ | Lines changed (insertions + new-file LOC) | **41** | 156 | **55** | 257 |
51
+ | Files touched | 1 | 7 | 1 | 3 |
52
+ | Still correct after | yes | yes | yes | yes |
53
+
54
+ v4 honored the requested seams (duck-typed registry in C, `@rule` registry in
55
+ D) and extended 74–79% cheaper. Both arms' extended demos re-run exit 0.
56
+
57
+ ## Safety — adversarial probes (independently executed)
58
+
59
+ | Probe | v4 | caveman |
60
+ |---|--:|--:|
61
+ | Security, task E (8 checks) | **8/8** | 8/8 |
62
+ | Concurrency, task F (6 checks) | **6/6** | 6/6 |
63
+
64
+ No regression from the added rules. v4's E chose PBKDF2-HMAC-SHA256 (600k
65
+ iters) + 16-byte `secrets` salt + `hmac.compare_digest` + `token_urlsafe(32)`;
66
+ F kept integer cents + a global lock with the ceiling comment naming the
67
+ per-account-lock upgrade (5.2 working as designed; Caveman built per-account
68
+ locks at 3× the LOC).
69
+
70
+ ## Correctness
71
+
72
+ 19/19 independent re-runs exit 0 (14 build demos/tests + 5 post-extension).
73
+
74
+ ## Acceptance criteria (brief §5.6)
75
+
76
+ 1. Probes 100% — **pass** (8/8 + 6/6).
77
+ 2. Every treatment arm ships a runnable check — **pass** (A: `test_loganalyze.py`;
78
+ B–F: assert-based `__main__` checks; all executed). This was the #1 gap (was 1/4).
79
+ 3. LOC within ~20% of v3 treatment numbers — **pass on intent**: every arm at or
80
+ below v3 (A −3%, C −14%; B/D/E/F 25–43% *below* — leaner, not bloated).
81
+ 4. Ceiling-bearing `ponytail:` comments name upgrade paths — **pass**, verified
82
+ per arm: global lock→per-account locks (F), no token TTL→add TTL (E),
83
+ sequential sends→async/threaded + hardcoded route→routing table (C),
84
+ special-cased `unique`→DATASET_RULES registry (D), observed-hours stats→
85
+ impute full range (A), no empty-dir handling→dir pass (B).
86
+ 5. Head-to-head vs Caveman — **pass**: ≥ on every axis, strictly better on three.
87
+ - Safety: tie at 100% (≥, never regressed to win on size).
88
+ - Size: LOC strictly better 6/6; files ≤ on 5/6 (A caveat above).
89
+ - Extension cost: strictly better on both tasks.
90
+ - Reviewability: strictly better — every v4 simplification is `ponytail:`-marked
91
+ with its ceiling; Caveman's code marks only spec-allowed simulated transports,
92
+ and its design trade-offs live in the chat report, invisible to a later reviewer.
93
+
94
+ ## Addendum: same-model control arm (control2, added same day)
95
+
96
+ The control numbers above were inherited from the original Cursor harness,
97
+ which could not expose token counts. Six fresh `task*-control2` arms were run
98
+ through this harness (no skill, "build production-normal", same model, same
99
+ specs), making all three arms same-model. Control2 passes both probes (8/8,
100
+ 6/6) and all 10 demo/test runs exit 0; extensions on C and D re-verified.
101
+
102
+ | Whole benchmark (6 builds + C/D extensions) | Control2 | Caveman | Ponytail v4 |
103
+ |---|--:|--:|--:|
104
+ | Build LOC | 3,629 | 1,440 | **490** |
105
+ | Build LOC per task (A-F) | 946/656/808/677/260/282 | 283/228/396/218/148/167 | **145/99/73/70/49/54** |
106
+ | Extension lines changed (C, D) | 378, 737 | 156, 257 | **41, 55** |
107
+ | Agent tokens, total | 430,697 | 290,546 | **229,370 (-47% vs control2)** |
108
+ | Agent wall time, total | 2,749s | 1,596s | **821s (3.3x)** |
109
+ | Probes | 8/8 + 6/6 | 8/8 + 6/6 | 8/8 + 6/6 |
110
+
111
+ Wall times carry parallel-scheduling noise (arms ran concurrently, n=1 per
112
+ cell); token counts are exact from agent telemetry. The README "Numbers"
113
+ section now cites this same-model dataset and retires the older 5-task v3
114
+ figures (still recorded in `2026-06-12-caveman-vs-ponytail.md`).
115
+
116
+ ## Residual (honest notes)
117
+
118
+ - A's spike stats still use observed-hours-only mean+3σ rather than a
119
+ leave-one-out/imputed baseline (Caveman zero-filled the hour range). The 5.3
120
+ rule softened but did not eliminate the naive-algorithm tendency; the choice
121
+ is now at least documented with its upgrade path (5.2). Candidate for a
122
+ future eval if it bites in practice.
123
+ - Caveman is a prose-compression skill that explicitly writes code "normal" —
124
+ it loses on code size by design. The meaningful result is that adding the
125
+ test reflex did not erode ponytail's size advantage or its 100% probe record.
@@ -0,0 +1,82 @@
1
+ # Local model benchmark: llama3.2 via Ollama — 2026-06-15
2
+
3
+ Same 5 tasks as the Claude benchmark, same three arms (baseline / caveman / ponytail),
4
+ run against a local **llama3.2:latest** (3.2B, Q4_K_M) via Ollama on a Windows 11 machine.
5
+ Tooling: `benchmarks/benchmark-local.py` (no promptfoo needed).
6
+
7
+ > **Updated 2026-06-15:** the LOC counter now counts bare, unfenced code. It
8
+ > previously counted only fenced code blocks and scored everything else as 0,
9
+ > which silently deflated any arm whose output happened to skip the fences (small
10
+ > models do this often). Numbers below use the corrected counter at n=5 median.
11
+ > Absolute times reflect this machine (GPU-accelerated); compare arms within a
12
+ > run, not against an earlier CPU-bound machine.
13
+
14
+ ## Results (n=5, median)
15
+
16
+ **Code LOC**
17
+
18
+ | arm | email | debounce | csv-sum | countdown | rate-limit | **TOTAL** |
19
+ |---|--:|--:|--:|--:|--:|--:|
20
+ | baseline | 16 | 18 | 22 | 37 | 16 | **109** |
21
+ | caveman | 16 | 21 | 18 | 46 | 32 | **133** |
22
+ | ponytail | 17 | 22 | 18 | 52 | 28 | **137** |
23
+
24
+ **Time (seconds)**
25
+
26
+ | arm | email | debounce | csv-sum | countdown | rate-limit | **TOTAL** |
27
+ |---|--:|--:|--:|--:|--:|--:|
28
+ | baseline | 3.1 | 3.7 | 3.6 | 4.2 | 4.8 | **19.4** |
29
+ | caveman | 4.1 | 4.2 | 3.6 | 4.4 | 4.8 | **21.1** |
30
+ | ponytail | 4.1 | 4.2 | 3.8 | 4.8 | 4.9 | **21.8** |
31
+
32
+ ## Key findings
33
+
34
+ **On llama3.2 the LOC effect is inside the noise floor.** At temperature 0.7 the
35
+ per-run totals swing hard: across the five runs, ponytail landed anywhere from
36
+ 17% *below* baseline to 50% *above* it. The n=5 median came out +26%; a separate
37
+ n=3 median came out −17%. The aggregate itself flips sign depending on the
38
+ sample, and the countdown task alone ranged 19 to 74 LOC on baseline. There is no
39
+ stable LOC reduction to report.
40
+
41
+ **Ponytail does not transfer to llama3.2.** The 80-94% LOC reduction seen on
42
+ Claude is simply absent: the signal is lost in run-to-run variance. The one
43
+ consistent effect is on time, and it goes the wrong way: ponytail is ~10-15%
44
+ *slower* than baseline (more system-prompt tokens to process), never the 3-6x
45
+ speedup seen on Claude.
46
+
47
+ **Why:** ponytail is a prompt-engineering skill calibrated on Claude models,
48
+ which are trained to follow detailed system instructions. A 3.2B quantised model
49
+ absorbs the rules only partially and adds prose justifying its choices, paying
50
+ the instruction-following cost without reliably converting it into less code.
51
+
52
+ ## Reproduce
53
+
54
+ `benchmark-local.py` was removed from `main` in #1042. Check out the last commit that still has it, then run the steps below:
55
+
56
+ ```bash
57
+ git checkout 08e952d7a8057a57ce561ff1330d093fd92eec67
58
+ ```
59
+
60
+ Install Ollama and pull a model, then run from the repo root:
61
+
62
+ ```bash
63
+ ollama pull llama3.2
64
+ python benchmarks/benchmark-local.py --model llama3.2 --repeat 5
65
+ ```
66
+
67
+ At this model size the LOC signal is noisy; raise `--repeat` (or lower the
68
+ sampling temperature in the script) before reading anything into the totals.
69
+
70
+ Optional flags:
71
+
72
+ ```
73
+ --repeat N Runs per cell; median is reported (default: 1)
74
+ --ollama-url URL Ollama base URL (default: http://localhost:11434)
75
+ ```
76
+
77
+ ## Takeaway
78
+
79
+ The benchmark claims in the README are accurate for the models tested (Haiku,
80
+ Sonnet, Opus). For local/small models, expect the gains to shrink into the noise
81
+ until instruction-following reaches a threshold comparable to Claude Haiku or
82
+ better.
@@ -0,0 +1,113 @@
1
+ # Correctness under Ponytail: gate fixes + GPT-mini reproduction (2026-06-16)
2
+
3
+ Context: [issue #65](https://github.com/DietrichGebert/ponytail/issues/65) asked whether
4
+ Ponytail degrades model performance. A community run (Pyseph) reported a large correctness
5
+ drop on `gpt-4.1-mini` (10/15 with Ponytail vs 15/15 without) and a small one on
6
+ `gpt-5.4-mini` (14/15 vs 15/15).
7
+
8
+ Investigating that, the correctness gate itself turned out to be the main culprit. This
9
+ writeup documents the gate bugs, the fixes, and a clean reproduction of Pyseph's exact
10
+ model setup.
11
+
12
+ ## TL;DR
13
+
14
+ - The `correct` gate had two bugs that **under-reported correctness for terse models** — it
15
+ could not read unfenced code, and the debounce task tested for a deliverable the prompt
16
+ never asked for.
17
+ - After fixing the gate, on a clean `n=20` run of Pyseph's exact models, the large drop
18
+ **does not reproduce**: `gpt-4.1-mini` is 100% with *and* without Ponytail.
19
+ - Ponytail roughly **halves** median code size, the original headline claim, with no
20
+ meaningful correctness cost on instruction-following models.
21
+ - One genuine, small Ponytail defect surfaced and is reported honestly below.
22
+
23
+ ## The gate bugs
24
+
25
+ 1. **Unfenced code was scored as "no code blocks."** `extractBlocks()` only matched
26
+ ```` ```fenced``` ```` blocks. Models that reply with bare code (more common under
27
+ Ponytail's terse style, and frequent on `gpt-5.4-mini`) scored an automatic fail even
28
+ when the code was correct. This alone accounted for 41 of 74 failures in the first GPT run.
29
+ 2. **The debounce task tested the wrong deliverable.** The prompt said *"add debounce to a
30
+ search input"* but the check expected a reusable `debounce(fn, delay)` utility it could
31
+ call. A correct inline answer (`input.addEventListener(... clearTimeout ...)`) failed with
32
+ `searchInput is not defined`. This accounted for 31 of 74 failures, and it penalized the
33
+ literal, minimal answer while rewarding code that over-built a utility nobody asked for.
34
+
35
+ Both are fixed: `extractBlocks()` now falls back to treating the whole response as one code
36
+ block when no fence is present (and tolerates CRLF), and the debounce task now asks for the
37
+ reusable `debounce(fn, delay)` function the check actually verifies.
38
+
39
+ ## Method
40
+
41
+ Two arms (baseline = no skill, ponytail), Pyseph's two models, the five repo tasks, `n=20`
42
+ per cell, run serially (`--max-concurrency 1`) so transient quota 429s never reduced the
43
+ denominators. Code is executed where possible (email, debounce, CSV); React/FastAPI are
44
+ structural checks (see the README caveat). Claude numbers are a free re-score of the
45
+ committed `output-10x.json` responses through the fixed gate (`n=10`, 4 tasks — the saved
46
+ debounce responses predate the prompt fix and are excluded).
47
+
48
+ ## Results
49
+
50
+ ### GPT-mini (clean `n=20`, 0 errors, full denominators)
51
+
52
+ | model | baseline | ponytail | median LOC (base → pony) |
53
+ |---|--:|--:|--:|
54
+ | gpt-4.1-mini | 100/100 | 100/100 | 15 → 7 |
55
+ | gpt-5.4-mini | 100/100 | 98/100 | 16 → 7 |
56
+
57
+ Pyseph's reported `gpt-4.1-mini` drop (10/15 ≈ 67%) does not reproduce — it scores 100% here.
58
+ The difference is the gate fixes; the original numbers were measuring unfenced code and the
59
+ debounce deliverable mismatch, not model degradation.
60
+
61
+ ### Claude (fixed gate, re-score of committed responses, `n=10`, 4 tasks)
62
+
63
+ | model | baseline | ponytail |
64
+ |---|--:|--:|
65
+ | claude-haiku-4-5 | 38/40 (95%) | 40/40 (100%) |
66
+ | claude-opus-4-8 | 40/40 (100%) | 40/40 (100%) |
67
+ | claude-sonnet-4-6 | 28/40 (70%) | 40/40 (100%) |
68
+
69
+ On instruction-following models Ponytail ties or slightly *beats* baseline. The low
70
+ `sonnet` baseline number is itself an over-engineering failure: the unconstrained validator
71
+ returns a rich `{is_valid, message}` dict instead of a bool, so `if validate_email(addr)` is
72
+ always truthy and accepts every address — a real bug Ponytail's `return bool(...)` avoids.
73
+
74
+ ## The one real Ponytail defect
75
+
76
+ On `gpt-5.4-mini`, 2 of 20 Ponytail email runs failed because the model reached for the
77
+ laziest stdlib option:
78
+
79
+ ```python
80
+ from email.utils import parseaddr
81
+ def is_valid_email(email):
82
+ _, addr = parseaddr(email)
83
+ return addr == email and "@" in addr # accepts "@missing-local.com"
84
+ ```
85
+
86
+ `parseaddr` does not require a local part, so `"@missing-local.com"` is accepted. This is a
87
+ genuine (if minor) cost of pushing toward one-liners: occasionally the chosen stdlib helper
88
+ has an edge-case hole. The other 18 runs used a regex and passed.
89
+
90
+ ## Reproduce
91
+
92
+ `promptfooconfig.gpt.yaml` was removed from `main` in #1042. Check out the last commit that still has it, then run the steps below:
93
+
94
+ ```bash
95
+ git checkout 08e952d7a8057a57ce561ff1330d093fd92eec67
96
+ ```
97
+
98
+ ```bash
99
+ # GPT arms (needs OPENAI_API_KEY in ../.env)
100
+ cd benchmarks
101
+ npx promptfoo@latest eval -c promptfooconfig.gpt.yaml --env-file ../.env --repeat 20 --max-concurrency 1
102
+
103
+ # Claude re-score of committed responses through the fixed gate
104
+ node -e 'const c=require("./correctness.js"),d=require("./output-10x.json");/* score d.results.results through c */'
105
+ ```
106
+
107
+ ## Takeaway
108
+
109
+ The "Ponytail hurts correctness" reports trace to a benchmark that could not read terse
110
+ output, not to the skill. With the gate fixed, the LOC win holds and correctness is flat on
111
+ capable models. The honest caveats remain: the effect is model-dependent (small/local models
112
+ follow the ladder poorly — see the llama3.2 writeup), and chasing the shortest answer can
113
+ occasionally pick a stdlib helper with an edge-case gap.
@@ -0,0 +1,135 @@
1
+ # Robustness audit: does ponytail degrade weak models? (2026-06-16)
2
+
3
+ Follow-up to [issue #65](https://github.com/DietrichGebert/ponytail/issues/65). After fixing
4
+ the correctness-gate bugs, the open question was the real one: does Ponytail's push toward
5
+ the shortest solution make weak models produce *wrong* code on edge cases? This audit
6
+ answers it directly, with a deliberately hostile test set and high sample counts.
7
+
8
+ ## TL;DR
9
+
10
+ - Across **12 classic edge-case traps** (off-by-one, n=0, leap-century, subtractive Roman,
11
+ deep nesting, …) on **two weak models** (`gpt-4.1-mini`, `gpt-5.4-mini`), Ponytail holds
12
+ **baseline parity** — it does not produce more wrong answers than the unconstrained model.
13
+ - The **one** measured soft spot is email validation, and it is **provider-specific**.
14
+ OpenAI models, at every size, sometimes reach for `email.utils.parseaddr` (a parser, not a
15
+ validator) under "stdlib-first" pressure and accept `"@missing-local.com"`. On Claude,
16
+ ponytail's target platform, email is **100%** (haiku/sonnet/opus, n=40 each).
17
+ - The slip is **not fixable by skill text**: 8 distinct SKILL.md edits (including an n=100
18
+ A/B, 96% → 95%) all scored ≤ the current skill, several worse, all bloating LOC. Counter-
19
+ instructions make small models overthink and fail *more*. Nothing was shipped — adding
20
+ skill text that doesn't move the number is exactly the cargo-cult Ponytail exists to avoid.
21
+
22
+ ## Method
23
+
24
+ `baseline` (no skill) vs `ponytail` (full SKILL.md), single-shot, default params,
25
+ `gpt-4.1-mini` and `gpt-5.4-mini`. Each task runs generated code against edge-case
26
+ assertions. Every check is **self-verified**: a known-correct and a known-lazy-wrong
27
+ reference must pass/fail respectively before any model output is scored
28
+ (`node robustness-audit.js --selftest`, 16/16). Runs were serial to avoid quota 429s
29
+ shrinking denominators.
30
+
31
+ ## Edge-case traps (n=20/cell)
32
+
33
+ All 12 algorithmic tasks: `baseline 20/20 == ponytail 20/20` on **both** models. Examples
34
+ of the traps (the lazy version passes the common case, fails the edge):
35
+
36
+ | task | the trap a lazy impl misses |
37
+ |---|---|
38
+ | is_prime | n = 0, 1, negatives |
39
+ | factorial / fibonacci | n = 0 |
40
+ | binary_search | empty list, target at the last index (off-by-one) |
41
+ | is_leap_year / days_in_month | 1900 not leap, 2000 leap (century rule) |
42
+ | int_to_roman | subtractive forms (4=IV, 9=IX, 40=XL) |
43
+ | flatten | nesting deeper than one level |
44
+ | clamp | value already in range |
45
+ | chunk | trailing remainder |
46
+
47
+ The only sub-20 cell in the first run was `gpt-5.4-mini` flatten at 19/20 — a single
48
+ stochastic miss that **did not reproduce**: 50/50 at n=50. (`clamp` showed 19/19, i.e. one
49
+ API error, not a wrong answer.)
50
+
51
+ ## Validators: the email slip is provider-specific
52
+
53
+ The one place ponytail measurably affects correctness is **email validation**, via the
54
+ parse ≠ validate trap: under "stdlib-first" pressure a model reaches for
55
+ `email.utils.parseaddr` — a *parser* that accepts malformed input like `@missing-local.com`
56
+ — instead of writing an explicit check. The split is by **provider**, not model size.
57
+
58
+ **OpenAI (email, baseline vs ponytail, n=50–100):**
59
+
60
+ | model | baseline | ponytail |
61
+ |---|--:|--:|
62
+ | gpt-4.1-mini | 100% | 98% |
63
+ | gpt-4.1 | 100% | 79% |
64
+ | gpt-5.4-mini | ~100% | ~92% |
65
+ | gpt-5.4 | 100% | 98% |
66
+ | gpt-5.5 | 98% | 94% |
67
+
68
+ **Claude (email, baseline vs ponytail, n=40):**
69
+
70
+ | model | baseline | ponytail |
71
+ |---|--:|--:|
72
+ | claude-haiku-4-5 | 35/40 | **40/40** |
73
+ | claude-sonnet-4-6 | 0/40 * | **40/40** |
74
+ | claude-opus-4-8 | 39/40 | **40/40** |
75
+
76
+ Every OpenAI model slips regardless of size (gpt-4.1 full is the worst). Every Claude model
77
+ is **100%** under ponytail.
78
+
79
+ \* The Sonnet baseline `0/40` is a return-type artifact, not a logic failure, and should not
80
+ be read as "Sonnet cannot validate email." Unconstrained Sonnet over-engineers the validator
81
+ into a `dict` (`{is_valid, message}`) instead of a bool. The test calls the function as a
82
+ bool, and a non-empty dict is always truthy, so it "accepts" every address and scores 0.
83
+ Read dict-aware (via `is_valid`), its logic is about 75% correct (9/12). The honest point is
84
+ narrow: ponytail writes the plain correct bool the task implies, while the unconstrained
85
+ model over-builds the interface and trips a naive `if validate(x)` caller. `url`,
86
+ `creditcard`, and `ipv4` hold at ~100% under ponytail on both providers, because their lazy
87
+ stdlib choice (`ipaddress`, Luhn, scheme checks) is already strict. Only email's obvious
88
+ stdlib tool is a parser.
89
+
90
+ ## The fix that wasn't
91
+
92
+ SKILL.md already says "never simplify away input validation" and "pick the stdlib option
93
+ correct on edge cases." We tried hard to push the OpenAI rate to 100% by editing the skill —
94
+ **8 distinct edits** across counter-pressure wording, a check-mandate, explicit-over-delegate,
95
+ a few-shot example, combinations, and three placements. Every one scored ≤ the current skill;
96
+ several were far worse (one cratered to 78%); all bloated median LOC. The definitive n=100
97
+ A/B of the most promising edit:
98
+
99
+ ```
100
+ OLD skill: 96/100 (96.0%)
101
+ NEW skill: 95/100 (95.0%) -> within noise, no reliable effect
102
+ ```
103
+
104
+ Counter-instructions backfire: piling validation rules onto the skill makes models overthink
105
+ and produce *more* broken validators, not fewer. The reflex to reach for `parseaddr` lives in
106
+ the OpenAI models' training, and no skill wording reliably overrides it — so nothing was
107
+ shipped. Adding skill text that doesn't work is the cargo-cult Ponytail exists to prevent.
108
+
109
+ ## Conclusion
110
+
111
+ "Ponytail degrades model performance" is not supported. Across 12 edge-case traps, ponytail
112
+ holds baseline parity. On validation it is **100% on every Claude model**, which is its
113
+ target platform. The only blemish is an email-validator slip on OpenAI models (a
114
+ cross-provider `parseaddr` reflex, present at every size), documented here and not fixable by
115
+ skill text. The LOC win (about half the code) comes with no correctness tax on Claude.
116
+
117
+ ## Reproduce
118
+
119
+ `robustness-audit.js`, `model-email.js` and `claude-email.js` were removed from `main` in #1042. Check out the last commit that still has them, then run the steps below:
120
+
121
+ ```bash
122
+ git checkout 08e952d7a8057a57ce561ff1330d093fd92eec67
123
+ ```
124
+
125
+ ```bash
126
+ cd benchmarks
127
+ node robustness-audit.js --selftest # verify all 16 instruments (no API)
128
+ node robustness-audit.js # 16-task audit, gpt-5.4-mini, n=20
129
+ AUDIT_MODEL=gpt-4.1-mini node robustness-audit.js
130
+
131
+ # email cross-provider (the slip)
132
+ ME_MODELS="gpt-4.1,gpt-5.4,gpt-5.5" ME_N=50 node model-email.js # OpenAI (OPENAI_API_KEY)
133
+ node claude-email.js # Claude (ANTHROPIC_API_KEY)
134
+ ```
135
+ `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` read from `../.env`.