Detection by execution

This is the core idea, and what makes Jollop different from buildpack-based platforms.

Static analysis only ranks guesses

When your code arrives, the detector looks at the files and produces ranked hypotheses: this looks like a Go module, this looks like Flask, this Makefile builds an executable. Dispatch is by manifest specificity, a file only one ecosystem emits decides before one several share (a package.json is both Node and Bun; the Bun lockfile breaks the tie), and long before a bare extension scan.

But that is only a ranking. The detector never decides how to run your app.

The probe proves the winner

The platform boots the top candidates in isolation and watches what actually happens:

  • Does it bind a port, and speak HTTP on it? → a web app.
  • Does it bind a port but not speak HTTP? → a raw TCP service.
  • Does it stay alive producing output without binding? → a worker.
  • Does it exit cleanly with output? → a job.
  • Does it crash, or never bind? → diagnose and repair, then retry.

Truth comes from observation, not from the manifest. That is why the awkward cases just work: a Flask app with no __main__ guard, a server with a hardcoded port, a lazy database connection that only fires on the first request, an unconventional entrypoint filename, a nested app directory.

Repair before the model

When a candidate fails, deterministic rules run first (missing dependency, wrong command, a port already in use, a database that needs provisioning). Only if the rules cannot fix it does a model get involved. Over time, model fixes that generalize become rules, so the share of deploys that never touch a model keeps climbing.

Freeze after first success

The moment an app goes green, its resolved plan is frozen. Redeploys of the same code replay the frozen plan instantly and never re-detect or re-repair. When the platform’s detection logic improves, a version stamp invalidates old plans so they re-resolve on their next deploy.

Shims: the one deliberate exception

A few frameworks (Flask, FastAPI) do not bind a port under their bare entrypoint. For these, prior knowledge ranks a small shim candidate first. This is the single place the platform uses a heuristic rather than pure observation, and it is deliberate.