◂ SCOPE CREEPER
BUILT IN PUBLIC · ENGINEERING WRITE-UP

I BUILT AN AI TOOL THAT TELLS ME
TO STOP BUILDING AI TOOLS

Scope Creeper is a diagnostic engine for builders who keep mistaking ambition for product. You drop in a GitHub repo URL, an AI chatlog, or one word; it returns a delusion score (0–100), a skill-tree of project paths your seed could grow into, and on-demand artifacts you can ship or paste into a real codebase. There is also a KILL artifact whose job is to tell you to stop.

What follows is the engineering write-up I wish I'd found before I started. Stack choices, the boring bits that ate three days, and the bits I'm most proud of. Live at scopecreeper.ai.

Why Llama 3.3 70B on Workers AI, not Claude or GPT

The single biggest decision. Two reasons.

One — every "wrap GPT-4 in a vibey UI" product gets knee-capped the day OpenAI changes pricing or rotates a key. I wanted a stack with no external paid-API dependency for the core loop. Cloudflare Workers AI ships @cf/meta/llama-3.3-70b-instruct-fp8-fast on the same edge runtime as the rest of the app, free at the tier we're at. The latency is fine. The JSON-mode reliability is shaky but workable (more on that).

Two — Llama 3.3 70B isn't as smart as Claude Opus on architectural reasoning, but it's plenty smart for what this product actually does: surfacing concrete project ideas from a fuzzy seed and writing a 1-page PRD with a stack and a 30-minute first cut. The intelligence delta matters less than the prompt engineering on top.

The JSON-mode "newline in string" gotcha

Workers AI's response_format: {type: "json_object"} returns structurally-valid JSON braces with one persistent flaw: the model emits literal \n characters inside string values, which is illegal per the JSON spec. JSON.parserejects it. I wasted half a day chasing "artifact generation failed" before I wrote a tolerant parser that walks the response character-by-character and escapes raw newlines, tabs, and carriage returns that appear inside string contexts.

function sanitizeJSONControlChars(s) {
  let out = "", inString = false, escape = false;
  for (const ch of s) {
    if (inString) {
      if (escape) { out += ch; escape = false; continue; }
      if (ch === "\\") { out += ch; escape = true; continue; }
      if (ch === '"') { out += ch; inString = false; continue; }
      if (ch === "\n") { out += "\\n"; continue; }
      if (ch === "\r") { out += "\\r"; continue; }
      if (ch === "\t") { out += "\\t"; continue; }
      out += ch;
    } else {
      out += ch;
      if (ch === '"') inString = true;
    }
  }
  return out;
}

Live at apps/web/src/lib/json-tolerant.ts. It's maybe forty lines and unblocked everything.

satori-on-edge for OG images, with a static fallback

I wanted satori-generated OG cards (per-thread, with the actual score baked in) so shared links would look good on X / iMessage / Slack. satori compiles to a SVG; @resvg/resvg-wasm turns that SVG into a PNG. Both work in Node. Both fight you on Cloudflare's edge runtime — next-on-pages bundles satori as a giant blob and the WASM module has loading quirks.

The shippable answer: pre-render the homepage OG card at build time via a Node script (scripts/build-og-root.mjs) and serve it as a static /og/root.png. For per-thread dynamic OG, the edge route does try satori + resvg-wasm but falls back to inline SVG if either fails. SVG OG previews work on Slack and Discord; Twitter/X and iMessage prefer PNG. Acceptable trade-off.

The hand-rolled tar parser for repo deep-audit

The Pro-tier deep-audit pulls a public repo's tarball from GitHub's codeload, walks every file, runs grep heuristics for TODO density, dead/skipped tests, leaked secrets, stale dependencies, and debug-spam in non-test source. Every finding cites real file + line numbers (no hallucinated paths — verifiable via git show HEAD:<file>).

Cloudflare Workers have a hard 30s wall clock and ~128MB memory. You can't buffer the whole tarball. So:

const decompressed = res.body.pipeThrough(new DecompressionStream("gzip"));
for await (const entry of walkTarGz(decompressed, {
  maxFiles: 200, maxTotalBytes: 30 * 1024 * 1024,
  maxFileBytes: 200 * 1024, wallMillis: 30_000,
  shouldEnter: isScannable,
})) {
  // grep + heuristics
}

The walker is ~120 lines of pure JS — tar is a 512-byte-aligned format that's genuinely easy to parse once you stop looking for an npm dependency. It pairs with DecompressionStream("gzip"), which is a Web standard Cloudflare ships. Total audit time on something like vercel/next.js: ~1.2s, with hard caps at 200 files / 30MB / 30s.

The four leaf artifacts (and why KILL is free)

Every dimension in the skill-tree can converge into one of four terminal artifacts:

The KILLartifact is the only one available on the free tier. That's deliberate: it's the artifact most likely to be screenshotted and shared, because telling someone to stop building is the meme. SHIPPABLE / ISSUE / BADGE are Pro-gated ($9/mo) — they're the artifacts people who actually want to ship will pay for.

Tier gating as data, not config

One charge(sid, env, opts) helper sits in front of every billable action. It checks legacy credits first (existing pack purchasers can still spend), then Pro entitlement, then the free-tier monthly quota. Failure modes return either TIER_LIMIT_REACHED (free user hit 5/mo cap) or PRO_REQUIRED (free user requested a Pro-gated artifact). The frontend reads session.isPro from /api/session and gates UI accordingly.

What I'd do differently

Try it

scopecreeper.ai — 5 free scans/month, no signup. The Hall of Delusion is live. Source pieces live at github.com/SipMyBeers/scopecreeper.