arr.includes() inside a loop
O(n²). 1k rows: fine. 100k rows: 3 seconds.
js-unique-via-indexofan agent skill · v0.1.0
59 deterministic rules. Finds O(n²) loops, N+1 queries, and brute force in AI output, before it ships.
fit for · embedded · iot · edge · zero api key · zero network
the rules and methodology aim your team at the top rows
What it catches
AI writes code that works on 1k rows and falls over at 100k. lemmaly flags the four most common ways.
arr.includes() inside a loop
O(n²). 1k rows: fine. 100k rows: 3 seconds.
js-unique-via-indexofawait query inside a for-loop
N+1. One request becomes one hundred.
js-await-in-for-looprecursion with no memoization
exponential. fib(40) freezes the tab.
js-recursion-no-memoSELECT without LIMIT
unbounded scan. Slows down as the table grows.
sql-select-no-limitCaptured · 498 real responses · 5 models · 10 tasks
Five frontier LLMs, ten runs per task, every response saved and classified for the exact anti-patterns lemmaly catches. The textbook prompts ask the model to optimize, and it does. The production prompts ask it to ship a feature; optimization is a detail it drops along the way. Code in /sandbox/ai-sessions.
Ask for `twoSum` and you get the one-pass hashmap. Optimization is what the prompt asks for; the model returns it. Training data is saturated, the bugs aren't here.
Express handlers against real tables. The model gets the structure right and the optimization wrong: N+1 imports, missing `LIMIT`, full-result fetches into memory. The bad shape is the easy shape when building is what you asked for.
%query% + no LIMITEach cell = X / 10 captured runs that shipped the anti-pattern. 0% = never. 100% = every response in 10 calls.
max_tokens = 4096. We save the whole response.arr.includes() in a loop is 195× slower than a Set at n = 100,000).classifiers/<task>.mjs) walks the response with regex / brace-tracking and decides hit-vs-miss. Tests in test-classifiers.mjs (17 pass).results/*.json + summary.json are committed. The 498 raw responses are reproducible from node capture.mjs and gitignored.The check runs while the AI is writing the code, not after it's already in production.
Both AIs were asked to write a dedupe. The one on the right was told to declare its complexity first.
The /complexity command makes the AI state the Big-O upfront, so the slow version never gets typed.
every algorithm chosen on purpose.
Each command maps to one discipline. Pick any and you can name the exact thing — no more "be more efficient".
Declare the cost before the code.
// Time: O(n) Space: O(n)
// Structure: Set for O(1) lookup
// Invariant: seen contains every prior element
function findDuplicates<T>(items: T[]): T[] {
const seen = new Set<T>();
const dups: T[] = [];
for (const x of items) {
if (seen.has(x)) dups.push(x);
else seen.add(x);
}
return dups;
}Prompts
Paste these into Claude Code, Cursor, or any agent that loaded the skill.
Use the lemmaly skill to check src/ for algorithmic risks. State the Big-O of any function over a collection, and call out N+1 queries, repeated scans, and brute-force loops with a named fix.
The simplest entry point. Skill auto-triggers on loops, queries, and collections; naming it explicitly guarantees it runs.
Use the lemmaly skill: run /complexity on findDuplicates(items: T[]). State Big-O, the data structure choice, and the loop invariant in comments before writing the body.
Forces the model to declare cost up front instead of reflexively reaching for a nested loop.
Use the lemmaly skill: run /n-plus-one on src/orders/list.ts. If it finds a repeated query inside a loop, follow up with /cut and rewrite using a single batched IN query.
Most AI-written request handlers ship with this exact pattern.
Use the lemmaly skill: run /ship-check on the current diff. Block merge if any new function is O(n²) without a comment justifying it, and flag any recursion without a stated base case.
Works as a review prompt or as the body of a pre-commit hook.
Use the lemmaly skill: run /scale-check on this dedupe at 10× and 100× today's 50k rows. Name the cliff and the next structure (HLL, MinHash, sweep line) when classical breaks.
For when “fast enough now” won't survive next quarter's traffic.
landscape
The only layer that briefs the agent before the loop is written.
Add the skill. Optional CLI for CI.
First line installs into the agent it detects (Claude Code, Cursor, Codex, Windsurf, etc.). Use --agent for Hermes, OpenClaw, or any of the 50+ supported targets. Add -g for user-global.
npx skills add morsechimwai/lemmaly
npx skills add morsechimwai/lemmaly --agent hermes-agent openclawlemmaly is open source, built by Morse Chimwai. Fork it, file issues, send PRs. Every rule, command, and discipline is on GitHub. Read the source, bend it to your stack, ship the parts you trust.