jevbooks

← All design patterns

Funnel

Cascade · 漏斗

Cheap first, expensive last

Line your judgments up by what each one costs, and let every item stop at the first stage that can settle it.

rulesallowallowLLM200308

The problem

You are building a code reviewer. One batch of changes handed to the team has 200 files in it, and you send every one of them to a large model and ask what is wrong with it.

A file takes a few seconds and a cent, so a run takes five minutes and a few dollars. The galling part is that 190 of those answers come back saying "nothing here". Nine tenths of your money and your waiting bought you a pile of "looks fine".

People fall into the opposite ditch too. A command-line guard that asks the model about every single command makes you wait 400 ms to list a directory. Something a one-line allowlist could have waved through was sent off for a judgment call.

The solution

The Funnel pattern says: line the judgments up by price — free code rules at the front, Jev in the middle, the expensive thing (a large model, or a person) at the end — and let each item stop at the first stage that can settle it.

The free stage may only short-circuit in the direction where being wrong is cheap. A command guard’s allowlist lets things through and never refuses: sudo, eval and redirections all go up. A reviewer’s free stage only skips files nobody needs to look at.

The middle stage lets Jev screen what is left with a few existence questions — one cheap request per item — and passes up only what clears the line, with a cap on how many. The expensive stage sees the survivors alone. Record which stage settled each item, or you will never be able to explain a verdict afterwards.

Ask · Answer · Act

  1. AskA command comes in. The allowlist looks at it first; only what the list cannot clear is sent on, together with the script it would run, under a handful of safety questions.
  2. AnswerThe most alarming of those questions comes back at 0.62.
  3. ActOver 0.45 but under 0.8, so it is neither refused nor allowed on its own: a person is asked to confirm, and a line is written down saying the second stage decided this one.

Where the name comes from

A funnel. Wide at the top, narrow at the bottom; the coarse stuff is caught up high and only the fine stuff travels down. No single layer has to catch everything — each one only does the cheap job that belongs to it.

Use it when

There are many items, most of them can be ruled out cheaply, and the stages have lopsided error costs: letting the wrong thing through and stopping the wrong thing do not hurt equally.

Not when

You have not measured what the screen misses — 63 of 80 real faults caught means that as a screen it loses 17 of them forever. Or the stage above short-circuits in the same direction as the one below, which means two stages are doing one job.

Projects that do this

  • jev-axi — CLI for TypeSafe's Jev: fast calibrated judgments from the shell.
  • jev-review — Staged code-review workflow with a local dashboard, gated by Jev.
  • abide — Make coding agents abide by project rules via Jev checks.

All projects with this pattern →

Read this code
  • jev-axisrc/safety.ts:93-147

    The free stage: a list of known-safe commands lets them through and is never allowed to refuse anything; sudo, eval and redirections always go up to Jev.

  • jev-reviewsrc/review/workflow.ts:58-65

    The cheap per-file screen is filtered by a threshold, sorted, and cut to a fixed number before anything reaches the expensive chain of follow-up judgments.

  • jev-reviewsrc/domain/config.ts:5-17

    0.7 and the top 8: the two numbers that stand between the cheap stage and the expensive one.

Show me the code
for (const cmd of commands) {
  if (ALLOWLIST.test(cmd) && !RISKY.test(cmd)) { log(cmd, "tier0-allow"); continue; }  // free: allow only
  const { answers } = await jev({ command: cmd, scripts: readScripts(cmd) }, SAFETY);  // the cheap screen
  const risk = Math.max(...Object.values(answers).map((a) => a.p));
  if (risk < 0.45) { log(cmd, "tier1-allow"); continue; }
  const verdict = risk >= 0.8 ? "deny" : await askPerson(cmd, answers);                // costly: the rest
  log(cmd, verdict);
}
How jevbooks recognises it

How do we know a project does this? We put one question about its README to Jev and it returns a percentage; the higher, the more it looks like it. If you copy the question, copy one thing: ask whether the text mentions this one concrete thing, never whether the project is good.

questions.json · sent for every README
{
  "cascade": {
    "type": "noul",
    "instructions": {
      "what": "Does the text describe a cheaper stage deciding which items reach a costlier one, such as code rules or an allowlist before Jev, or a Jev screen whose positives go to an LLM or a person?",
      "not_for": "Every item going through the same single stage."
    },
    "criteria": {
      "true": "The text explicitly describes this. One sentence is enough; other content does not cancel it.",
      "false": "The text does not describe this."
    }
  }
}

Build this with Jev →