Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
426ffd0b36 | ||
|
|
bd6176a9b3 |
@@ -32,6 +32,6 @@ End with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. S
|
|||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
||||||
and performance are explicitly out of scope — route them to a normal review
|
and performance are explicitly out of scope. Route them to a normal review
|
||||||
pass. Lists findings, applies nothing. One-shot.
|
pass. Lists findings, applies nothing. One-shot.
|
||||||
"stop ponytail-audit" or "normal mode" to revert.
|
"stop ponytail-audit" or "normal mode" to revert.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ the convention out of the ledger.
|
|||||||
|
|
||||||
One row per marker, grouped by file:
|
One row per marker, grouped by file:
|
||||||
|
|
||||||
`<file>:<line> — <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
|
`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
|
||||||
|
|
||||||
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
|
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
|
||||||
and the trigger straight from the comment. Want an owner per row too? add
|
and the trigger straight from the comment. Want an owner per row too? add
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ If there is nothing to cut, say `Lean already. Ship.` and stop.
|
|||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
||||||
and performance are explicitly out of scope — route them to a normal review
|
and performance are explicitly out of scope. Route them to a normal review
|
||||||
pass, not this one. A single smoke test or `assert`-based
|
pass, not this one. A single smoke test or `assert`-based
|
||||||
self-check is the ponytail minimum, not bloat, never flag it for deletion.
|
self-check is the ponytail minimum, not bloat, never flag it for deletion.
|
||||||
Does not apply the fixes, only lists them.
|
Does not apply the fixes, only lists them.
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
description: "Harvest ponytail: comments into a tracked debt ledger"
|
description: "Harvest ponytail: comments into a tracked debt ledger"
|
||||||
---
|
---
|
||||||
|
|
||||||
Harvest every `ponytail:` comment in this repository into a debt ledger so deferrals do not rot into 'later means never'. Grep the whole tree for comment markers (grep -rnE '(#|//) ?ponytail:' ., skipping node_modules/.git/build output). One row per marker, grouped by file: <file>:<line> — <what was simplified>. ceiling: <the limit named in the comment>. upgrade: <the trigger to revisit>. Tag any marker that names no upgrade path or trigger as no-trigger, those rot silently. End with the count of markers and how many lack a trigger. If none: 'No ponytail: debt. Clean ledger.' Report only, change nothing.
|
Harvest every `ponytail:` comment in this repository into a debt ledger so deferrals do not rot into 'later means never'. Grep the whole tree for comment markers (grep -rnE '(#|//) ?ponytail:' ., skipping node_modules/.git/build output). One row per marker, grouped by file: <file>:<line>, <what was simplified>. ceiling: <the limit named in the comment>. upgrade: <the trigger to revisit>. Tag any marker that names no upgrade path or trigger as no-trigger, those rot silently. End with the count of markers and how many lack a trigger. If none: 'No ponytail: debt. Clean ledger.' Report only, change nothing.
|
||||||
|
|||||||
+2
-2
@@ -96,7 +96,7 @@ Flojo, no negligente: la validación en límites de confianza, el manejo de pér
|
|||||||
|
|
||||||
El mayor esfuerzo que ponytail te va a pedir:
|
El mayor esfuerzo que ponytail te va a pedir:
|
||||||
|
|
||||||
Los plugins de Claude Code y Codex ejecutan dos pequeños lifecycle hooks de Node.js, así que `node` debe estar en tu PATH (nota para usuarios de Nix/nvm: debe estar en el PATH del shell no-interactivo). Si no lo está, los skills igualmente funcionan — la activación automática simplemente queda en silencio en vez de lanzar un error en cada prompt.
|
Los plugins de Claude Code y Codex ejecutan dos pequeños lifecycle hooks de Node.js, así que `node` debe estar en tu PATH (nota para usuarios de Nix/nvm: debe estar en el PATH del shell no-interactivo). Si no lo está, los skills igualmente funcionan, la activación automática simplemente queda en silencio en vez de lanzar un error en cada prompt.
|
||||||
|
|
||||||
### Claude Code
|
### Claude Code
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ node scripts/check-rule-copies.js
|
|||||||
npm test
|
npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
El paquete de skills de OpenClaw (`.openclaw/skills/`) se genera desde `skills/`; ejecuta `node scripts/build-openclaw-skills.js` después de cambiar un skill — la suite de tests falla si está desactualizado.
|
El paquete de skills de OpenClaw (`.openclaw/skills/`) se genera desde `skills/`; ejecuta `node scripts/build-openclaw-skills.js` después de cambiar un skill, la suite de tests falla si está desactualizado.
|
||||||
|
|
||||||
El benchmark de correctness lanza Python para las verificaciones de email y CSV; se prueba `python3` antes que `python`. Las verificaciones de CSV requieren `pandas` instalado localmente.
|
El benchmark de correctness lanza Python para las verificaciones de email y CSV; se prueba `python3` antes que `python`. Las verificaciones de CSV requieren `pandas` instalado localmente.
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ It reuses this repo's `gemini-extension.json`. One difference: Antigravity conve
|
|||||||
|
|
||||||
### CodeWhale
|
### CodeWhale
|
||||||
|
|
||||||
Reads `AGENTS.md` from the project root — zero setup. Copy [`AGENTS.md`](AGENTS.md) to your project, or run `codewhale` from a checkout of this repo. That's it.
|
Reads `AGENTS.md` from the project root, zero setup. Copy [`AGENTS.md`](AGENTS.md) to your project, or run `codewhale` from a checkout of this repo. That's it.
|
||||||
|
|
||||||
### OpenClaw
|
### OpenClaw
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Three arms (no skill, [caveman](https://github.com/JuliusBrussee/caveman), ponyt
|
|||||||
|
|
||||||
### Claude (Haiku / Sonnet / Opus)
|
### Claude (Haiku / Sonnet / Opus)
|
||||||
|
|
||||||
Requires an Anthropic API key and **Node.js ≥ 22.22.0** (promptfoo's engine constraint —
|
Requires an Anthropic API key and **Node.js ≥ 22.22.0** (promptfoo's engine constraint,
|
||||||
check with `node --version` and upgrade if needed):
|
check with `node --version` and upgrade if needed):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ python judge.py --run runs/<stamp> # score every workspace's source
|
|||||||
|
|
||||||
Fewer lines only counts as a win if the code still does the job. The LOC tier scores the open
|
Fewer lines only counts as a win if the code still does the job. The LOC tier scores the open
|
||||||
feature tasks on `git diff` alone, with no deterministic check that the asked feature was
|
feature tasks on `git diff` alone, with no deterministic check that the asked feature was
|
||||||
actually built — so an arm could "win" the LOC metric by shipping a stub. This pass closes that
|
actually built, so an arm could "win" the LOC metric by shipping a stub. This pass closes that
|
||||||
hole: the same auditable LLM judge (fixed model, temperature 0, published rubric) rates how
|
hole: the same auditable LLM judge (fixed model, temperature 0, published rubric) rates how
|
||||||
**fully** each submission implements its task. Rubric: `0` stub/placeholder, `1` partial (core
|
**fully** each submission implements its task. Rubric: `0` stub/placeholder, `1` partial (core
|
||||||
behavior missing), `2` mostly complete (a stated requirement missing), `3` fully implements the
|
behavior missing), `2` mostly complete (a stated requirement missing), `3` fully implements the
|
||||||
task. Read it **alongside** the LOC table — a low-LOC arm whose completeness also drops is doing
|
task. Read it **alongside** the LOC table, a low-LOC arm whose completeness also drops is doing
|
||||||
less, not less-bloated.
|
less, not less-bloated.
|
||||||
|
|
||||||
Validated like the over-engineering judge: `--selftest` requires the judge to rank a complete
|
Validated like the over-engineering judge: `--selftest` requires the judge to rank a complete
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Libraries people install that the runtime already ships.
|
|||||||
| `query-string` / `qs` | `new URLSearchParams(location.search)` |
|
| `query-string` / `qs` | `new URLSearchParams(location.search)` |
|
||||||
| `lodash.clonedeep` | `structuredClone(obj)` |
|
| `lodash.clonedeep` | `structuredClone(obj)` |
|
||||||
| `lodash.groupby` | `Object.groupBy(arr, fn)` |
|
| `lodash.groupby` | `Object.groupBy(arr, fn)` |
|
||||||
| `lodash.debounce` | — see debounce one-liner below |
|
| `lodash.debounce` | see debounce one-liner below |
|
||||||
| `numeral` / `accounting` | `new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })` |
|
| `numeral` / `accounting` | `new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })` |
|
||||||
| `date-fns` format | `new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date)` |
|
| `date-fns` format | `new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date)` |
|
||||||
| `date-fns` relative time | `new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-3, "day")` |
|
| `date-fns` relative time | `new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-3, "day")` |
|
||||||
@@ -97,7 +97,7 @@ Packages that wrap Node built-ins.
|
|||||||
| `make-dir` | `fs.mkdirSync(path, { recursive: true })` |
|
| `make-dir` | `fs.mkdirSync(path, { recursive: true })` |
|
||||||
| `slash` (win paths) | `path.posix` or `path.normalize()` |
|
| `slash` (win paths) | `path.posix` or `path.normalize()` |
|
||||||
| `uuid` (v4) | `crypto.randomUUID()` |
|
| `uuid` (v4) | `crypto.randomUUID()` |
|
||||||
| `ms` (parse duration strings) | — keep `ms`, it's genuinely useful and tiny |
|
| `ms` (parse duration strings) | keep `ms`, it's genuinely useful and tiny |
|
||||||
| `is-stream` | `val instanceof stream.Readable` |
|
| `is-stream` | `val instanceof stream.Readable` |
|
||||||
| `object-assign` | `Object.assign()` / spread |
|
| `object-assign` | `Object.assign()` / spread |
|
||||||
| `array-uniq` | `[...new Set(arr)]` |
|
| `array-uniq` | `[...new Set(arr)]` |
|
||||||
@@ -119,12 +119,12 @@ Packages that wrap what Python already ships.
|
|||||||
| `python-dateutil` (basic parsing) | `datetime.fromisoformat()` (Python 3.7+) |
|
| `python-dateutil` (basic parsing) | `datetime.fromisoformat()` (Python 3.7+) |
|
||||||
| `pytz` | `zoneinfo.ZoneInfo("America/New_York")` (Python 3.9+) |
|
| `pytz` | `zoneinfo.ZoneInfo("America/New_York")` (Python 3.9+) |
|
||||||
| `attrs` (simple data classes) | `@dataclass` |
|
| `attrs` (simple data classes) | `@dataclass` |
|
||||||
| `six` | — drop it, Python 2 is gone |
|
| `six` | drop it, Python 2 is gone |
|
||||||
| `pathlib2` | `pathlib.Path` (built-in since Python 3.4) |
|
| `pathlib2` | `pathlib.Path` (built-in since Python 3.4) |
|
||||||
| `enum34` | `enum.Enum` (built-in since Python 3.4) |
|
| `enum34` | `enum.Enum` (built-in since Python 3.4) |
|
||||||
| `typing_extensions` (common types) | `from __future__ import annotations` + built-in generics |
|
| `typing_extensions` (common types) | `from __future__ import annotations` + built-in generics |
|
||||||
| `simplejson` (basic use) | `json` (stdlib) |
|
| `simplejson` (basic use) | `json` (stdlib) |
|
||||||
| `requests` (simple GET) | `urllib.request.urlopen(url)` — `requests` for anything real |
|
| `requests` (simple GET) | `urllib.request.urlopen(url)`, `requests` for anything real |
|
||||||
| `click` (single command) | `argparse` (stdlib) |
|
| `click` (single command) | `argparse` (stdlib) |
|
||||||
| `mergedeep` | `dict \| other_dict` (Python 3.9+) |
|
| `mergedeep` | `dict \| other_dict` (Python 3.9+) |
|
||||||
| `more-itertools` (basic) | `itertools` (stdlib): `chain`, `islice`, `groupby`, `product` |
|
| `more-itertools` (basic) | `itertools` (stdlib): `chain`, `islice`, `groupby`, `product` |
|
||||||
@@ -150,9 +150,9 @@ Things the application layer implements that the database already does.
|
|||||||
| JSON storage + query | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) |
|
| JSON storage + query | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) |
|
||||||
| UUID generation | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) |
|
| UUID generation | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) |
|
||||||
| Timestamps on insert/update | `DEFAULT now()` + trigger or `ON UPDATE CURRENT_TIMESTAMP` |
|
| Timestamps on insert/update | `DEFAULT now()` + trigger or `ON UPDATE CURRENT_TIMESTAMP` |
|
||||||
| Enforce uniqueness | `UNIQUE` constraint — not application-level checks |
|
| Enforce uniqueness | `UNIQUE` constraint, not application-level checks |
|
||||||
| Enforce referential integrity | `FOREIGN KEY` — not application-level checks |
|
| Enforce referential integrity | `FOREIGN KEY`, not application-level checks |
|
||||||
| Enforce value ranges | `CHECK (price > 0)` — not application-level validation |
|
| Enforce value ranges | `CHECK (price > 0)`, not application-level validation |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -170,4 +170,4 @@ You debug the wrapper.
|
|||||||
|
|
||||||
Skip the wrapper. The platform ships with your app for free.
|
Skip the wrapper. The platform ships with your app for free.
|
||||||
|
|
||||||
When the native solution is genuinely insufficient (old browser support, edge cases it doesn't handle, ergonomics that matter at scale), the library earns its place. Install it then — not before.
|
When the native solution is genuinely insufficient (old browser support, edge cases it doesn't handle, ergonomics that matter at scale), the library earns its place. Install it then, not before.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
Real model output, verbatim from benchmark runs — the same task answered by the same model
|
Real model output, verbatim from benchmark runs, the same task answered by the same model
|
||||||
with no skill (`## Without Ponytail`) and with ponytail (`## With Ponytail`), so you can
|
with no skill (`## Without Ponytail`) and with ponytail (`## With Ponytail`), so you can
|
||||||
compare side by side. Model: Claude Haiku 4.5, temperature 1, source `benchmarks/output.json`.
|
compare side by side. Model: Claude Haiku 4.5, temperature 1, source `benchmarks/output.json`.
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
**Task:** "Write Python code that reads sales.csv and sums the 'amount' column."
|
**Task:** "Write Python code that reads sales.csv and sums the 'amount' column."
|
||||||
|
|
||||||
Verbatim model output from a benchmark run — Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
||||||
|
|
||||||
## Without Ponytail — 20 lines of code
|
## Without Ponytail, 20 lines of code
|
||||||
|
|
||||||
# Python code to read sales.csv and sum the 'amount' column
|
# Python code to read sales.csv and sum the 'amount' column
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ except KeyError:
|
|||||||
- Provides built-in error handling
|
- Provides built-in error handling
|
||||||
- Efficient for large files
|
- Efficient for large files
|
||||||
|
|
||||||
## With Ponytail — 3 lines of code
|
## With Ponytail, 3 lines of code
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import csv
|
import csv
|
||||||
@@ -66,6 +66,6 @@ total = sum(float(row['amount']) for row in csv.DictReader(open('sales.csv')))
|
|||||||
print(total)
|
print(total)
|
||||||
```
|
```
|
||||||
|
|
||||||
Skipped: pandas, error handling, file closing — add when the CSV is large, malformed, or you need more analysis.
|
Skipped: pandas, error handling, file closing, add when the CSV is large, malformed, or you need more analysis.
|
||||||
|
|
||||||
**20 → 3 lines of code** — same model, same prompt.
|
**20 → 3 lines of code**, same model, same prompt.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
**Task:** "Add debounce to a search input in vanilla JavaScript. It currently fires an API call on every keystroke."
|
**Task:** "Add debounce to a search input in vanilla JavaScript. It currently fires an API call on every keystroke."
|
||||||
|
|
||||||
Verbatim model output from a benchmark run — Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
||||||
|
|
||||||
## Without Ponytail — 116 lines of code
|
## Without Ponytail, 116 lines of code
|
||||||
|
|
||||||
# Debounce Search Input
|
# Debounce Search Input
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ searchInput.addEventListener('blur', () => {
|
|||||||
|
|
||||||
**Typical keystroke reduction:** 10 characters = 10 API calls → 1-2 API calls with debounce ✅
|
**Typical keystroke reduction:** 10 characters = 10 API calls → 1-2 API calls with debounce ✅
|
||||||
|
|
||||||
## With Ponytail — 10 lines of code
|
## With Ponytail, 10 lines of code
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const searchInput = document.querySelector('input[type="search"]');
|
const searchInput = document.querySelector('input[type="search"]');
|
||||||
@@ -206,6 +206,6 @@ searchInput.addEventListener('input', (e) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Skipped: debounce utility function, class wrapper, config object — `setTimeout` + `clearTimeout` is the debounce. Add a utility when you need it on 3+ inputs.
|
Skipped: debounce utility function, class wrapper, config object, `setTimeout` + `clearTimeout` is the debounce. Add a utility when you need it on 3+ inputs.
|
||||||
|
|
||||||
**116 → 10 lines of code** — same model, same prompt.
|
**116 → 10 lines of code**, same model, same prompt.
|
||||||
|
|||||||
@@ -28,4 +28,4 @@ const copy = JSON.parse(JSON.stringify(original));
|
|||||||
const copy = structuredClone(original);
|
const copy = structuredClone(original);
|
||||||
```
|
```
|
||||||
|
|
||||||
**1 dependency (or a fragile hack) → 1 built-in.** `structuredClone` handles `Date`, `Map`, `Set`, `ArrayBuffer`, `RegExp`, circular references, and more — everything `JSON.parse/stringify` silently drops. Available in every browser since 2022 and Node.js since v17. Pull lodash in when you need the rest of it, not for one function.
|
**1 dependency (or a fragile hack) → 1 built-in.** `structuredClone` handles `Date`, `Map`, `Set`, `ArrayBuffer`, `RegExp`, circular references, and more, everything `JSON.parse/stringify` silently drops. Available in every browser since 2022 and Node.js since v17. Pull lodash in when you need the rest of it, not for one function.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
**Task:** "Write me a Python function that validates email addresses."
|
**Task:** "Write me a Python function that validates email addresses."
|
||||||
|
|
||||||
Verbatim model output from a benchmark run — Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
||||||
|
|
||||||
## Without Ponytail — 75 lines of code
|
## Without Ponytail, 75 lines of code
|
||||||
|
|
||||||
# Email Validation Function
|
# Email Validation Function
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
**Recommendation**: Use the `email-validator` library for production applications, as it properly handles RFC 5321/5322 standards. Use the simple regex version for quick scripts or when you want to avoid dependencies.
|
**Recommendation**: Use the `email-validator` library for production applications, as it properly handles RFC 5321/5322 standards. Use the simple regex version for quick scripts or when you want to avoid dependencies.
|
||||||
|
|
||||||
## With Ponytail — 3 lines of code
|
## With Ponytail, 3 lines of code
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import re
|
import re
|
||||||
@@ -151,6 +151,6 @@ def is_valid_email(email: str) -> bool:
|
|||||||
return bool(re.match(r'^[^@]+@[^@]+\.[^@]+$', email))
|
return bool(re.match(r'^[^@]+@[^@]+\.[^@]+$', email))
|
||||||
```
|
```
|
||||||
|
|
||||||
Skipped: RFC 5322 parser, DNS MX lookup, confirmation email. Add when you actually need to reject `user+tag@sub.domain.co.uk` or catch typos — until then, this catches 99% of "oops I fat-fingered it" cases.
|
Skipped: RFC 5322 parser, DNS MX lookup, confirmation email. Add when you actually need to reject `user+tag@sub.domain.co.uk` or catch typos, until then, this catches 99% of "oops I fat-fingered it" cases.
|
||||||
|
|
||||||
**75 → 3 lines of code** — same model, same prompt.
|
**75 → 3 lines of code**, same model, same prompt.
|
||||||
|
|||||||
@@ -32,4 +32,4 @@ const byStatus = Object.groupBy(orders, order => order.status);
|
|||||||
// → { pending: [...], shipped: [...], delivered: [...] }
|
// → { pending: [...], shipped: [...], delivered: [...] }
|
||||||
```
|
```
|
||||||
|
|
||||||
**1 dependency (or a reduce) → 1 built-in.** `Object.groupBy` shipped in Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If you need a `Map` instead of a plain object: `Map.groupBy(orders, o => o.status)`. Check your target runtime; if you need IE11 or old Node, the `reduce` one-liner is still the right call — not lodash.
|
**1 dependency (or a reduce) → 1 built-in.** `Object.groupBy` shipped in Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If you need a `Map` instead of a plain object: `Map.groupBy(orders, o => o.status)`. Check your target runtime; if you need IE11 or old Node, the `reduce` one-liner is still the right call, not lodash.
|
||||||
|
|||||||
@@ -55,4 +55,4 @@ export function Feed({ items, fetchMore, hasMore }) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**1 dependency → 0 dependencies.** `IntersectionObserver` fires only when the sentinel enters the viewport — no scroll event, no throttling, no jank. Ships in every browser. The library wraps exactly this API.
|
**1 dependency → 0 dependencies.** `IntersectionObserver` fires only when the sentinel enters the viewport, no scroll event, no throttling, no jank. Ships in every browser. The library wraps exactly this API.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export function DeleteModal({ onConfirm, onCancel }) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A dependency, a portal, an overlay, a root, a trigger, a content wrapper — to show a box with two buttons.
|
A dependency, a portal, an overlay, a root, a trigger, a content wrapper, to show a box with two buttons.
|
||||||
|
|
||||||
## With Ponytail
|
## With Ponytail
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
**Task:** "Add rate limiting to my FastAPI endpoint so users can't spam it."
|
**Task:** "Add rate limiting to my FastAPI endpoint so users can't spam it."
|
||||||
|
|
||||||
Verbatim model output from a benchmark run — Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
||||||
|
|
||||||
## Without Ponytail — 128 lines of code
|
## Without Ponytail, 128 lines of code
|
||||||
|
|
||||||
# Rate Limiting in FastAPI
|
# Rate Limiting in FastAPI
|
||||||
|
|
||||||
@@ -248,9 +248,9 @@ async def test_rate_limit():
|
|||||||
# First 5 should be 200, 6th should be 429
|
# First 5 should be 200, 6th should be 429
|
||||||
```
|
```
|
||||||
|
|
||||||
**Recommendation:** Use `slowapi` for most cases—it's the most popular and easiest to implement! 🚀
|
**Recommendation:** Use `slowapi` for most cases, it's the most popular and easiest to implement! 🚀
|
||||||
|
|
||||||
## With Ponytail — 10 lines of code
|
## With Ponytail, 10 lines of code
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -267,6 +267,6 @@ async def my_endpoint(request):
|
|||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
```
|
```
|
||||||
|
|
||||||
Skipped: custom rate limit logic, Redis, sliding windows — `slowapi` handles it. Add when: you need distributed rate limiting across multiple servers (swap `Limiter` for Redis backend) or per-user limits (add `key_func=lambda r: r.headers.get("authorization")`).
|
Skipped: custom rate limit logic, Redis, sliding windows, `slowapi` handles it. Add when: you need distributed rate limiting across multiple servers (swap `Limiter` for Redis backend) or per-user limits (add `key_func=lambda r: r.headers.get("authorization")`).
|
||||||
|
|
||||||
**128 → 10 lines of code** — same model, same prompt.
|
**128 → 10 lines of code**, same model, same prompt.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
**Task:** "Build me a countdown timer component in React that counts down from a given number of seconds."
|
**Task:** "Build me a countdown timer component in React that counts down from a given number of seconds."
|
||||||
|
|
||||||
Verbatim model output from a benchmark run — Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
|
||||||
|
|
||||||
## Without Ponytail — 267 lines of code
|
## Without Ponytail, 267 lines of code
|
||||||
|
|
||||||
# React Countdown Timer Component
|
# React Countdown Timer Component
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ export function StyledCountdownTimer({ initialSeconds = 60 }) {
|
|||||||
|
|
||||||
Choose the version that best fits your needs!
|
Choose the version that best fits your needs!
|
||||||
|
|
||||||
## With Ponytail — 9 lines of code
|
## With Ponytail, 9 lines of code
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
export function CountdownTimer({ seconds }) {
|
export function CountdownTimer({ seconds }) {
|
||||||
@@ -385,6 +385,6 @@ export function CountdownTimer({ seconds }) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Skipped: pause/resume, formatted display (mm:ss), sound on zero, styling — add when needed.
|
Skipped: pause/resume, formatted display (mm:ss), sound on zero, styling, add when needed.
|
||||||
|
|
||||||
**267 → 9 lines of code** — same model, same prompt.
|
**267 → 9 lines of code**, same model, same prompt.
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ prompt menu, or that pull context through tools. See issue #70.
|
|||||||
|
|
||||||
## What it exposes
|
## What it exposes
|
||||||
|
|
||||||
- Prompt `ponytail` — returns the ruleset as a user message. Optional `mode`
|
- Prompt `ponytail`, returns the ruleset as a user message. Optional `mode`
|
||||||
argument: `lite`, `full`, or `ultra`. Omit it to use the configured default.
|
argument: `lite`, `full`, or `ultra`. Omit it to use the configured default.
|
||||||
- Tool `ponytail_instructions` — same text, plus `structuredContent`
|
- Tool `ponytail_instructions`, same text, plus `structuredContent`
|
||||||
(`{ mode, instructions }`), for hosts that pull context via tools or code
|
(`{ mode, instructions }`), for hosts that pull context via tools or code
|
||||||
execution. Read-only.
|
execution. Read-only.
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ End with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. S
|
|||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
||||||
and performance are explicitly out of scope — route them to a normal review
|
and performance are explicitly out of scope. Route them to a normal review
|
||||||
pass. Lists findings, applies nothing. One-shot.
|
pass. Lists findings, applies nothing. One-shot.
|
||||||
"stop ponytail-audit" or "normal mode" to revert.
|
"stop ponytail-audit" or "normal mode" to revert.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ the convention out of the ledger.
|
|||||||
|
|
||||||
One row per marker, grouped by file:
|
One row per marker, grouped by file:
|
||||||
|
|
||||||
`<file>:<line> — <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
|
`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
|
||||||
|
|
||||||
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
|
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
|
||||||
and the trigger straight from the comment. Want an owner per row too? add
|
and the trigger straight from the comment. Want an owner per row too? add
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ If there is nothing to cut, say `Lean already. Ship.` and stop.
|
|||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
Scope: over-engineering and complexity only. Correctness bugs, security holes,
|
||||||
and performance are explicitly out of scope — route them to a normal review
|
and performance are explicitly out of scope. Route them to a normal review
|
||||||
pass, not this one. A single smoke test or `assert`-based
|
pass, not this one. A single smoke test or `assert`-based
|
||||||
self-check is the ponytail minimum, not bloat, never flag it for deletion.
|
self-check is the ponytail minimum, not bloat, never flag it for deletion.
|
||||||
Does not apply the fixes, only lists them.
|
Does not apply the fixes, only lists them.
|
||||||
|
|||||||
Reference in New Issue
Block a user