Skip to content
MLReps

Codebase intelligence

See how your software
actually works.

Your architecture, your slow paths, your risky files — one map built from the repository you already have and the traffic it already serves. Every answer points at the line of code that proves it.

  • Know what a change will reach before you merge it
  • Find the one query that is costing you the request
  • Hand your coding agent a prompt that is already correct

Runs on your machine firstNothing stored, nothing trained onOne repository at a time

An example application· 519 files · 1,806 linksexample, not a scan
SCREENSAPISERVER CODEDATAcheckout/pagecart/pageadmin/ordersinvoices/page/api/checkout/api/ordersOrderService.tsPriceRules.tsinvoice/render.tsordersuserspayments
one request pathnot measured yetslowest route on the map

Most of the request

is one database read

1 critical finding

a live key in source

Built fora lead who inherited 400 filesa team shipping faster than it can reviewa founder who needs to know what was builtScroll — the next screen is the product

01 · What you are working with

A file list is not an answer.

01519 files. Which one owns checkout?Folders are how it is stored, not how it runs.
02The same rounding rule exists twice — and they disagree.One was fixed in March. The other was not.
03A screen talks straight to the database, past the API.Faster that afternoon. Still there two quarters later.
04One file is holding up twenty-six others.Nobody can say that from a file tree, so every change is a bet.
05Five problems in one screen — and none are in the names.This is the work MLReps does on every push.
an example application· as your editor shows it5 problems

checkout/page.tsx

screen

cart/page.tsx

screen

CartRow.tsx

component

PriceTag.tsx

component

api/checkout

route

api/orders

route

api/invoice

route

api/auth

route

OrderService.ts

318 lines

26 dependants

PriceRules.ts

142 lines

discount.ts

96 lines

copy

auth/session.ts

74 lines

invoice/render.ts

88 lines

mailer.ts

51 lines

utils/money.ts

31 lines

copy

nightly-settle.ts

job

db · orders

table

db · users

table

db · invoices

table

legacy/payments.js

unread

same rule, twice
screen → database, directly

Two duplicates, one shortcut, one overloaded file, one region nothing can read.You would have found these in a week. The scan finds them in one pass, on every push.

So this is what MLReps does with it

One pass over the code you already have. Four lenses on the same map. No guessing.

A deterministic analysis builds the graph first. Language only comes in at the end, to word what the analysis already found — so the answer survives the model being switched off.

Architecture

What exists, what calls what, and what one change will reach.

Performance

Your own traces, joined to the exact file that spent the time.

Risk

Secrets in source, reads with no authorisation, dangerous calls.

Optimization

Work done twice, work done in a loop, work done on everything.

02 · It reads, then it understands

Watch a folder become a system.

01One command, and it starts reading 141 files.Every file an adapter can read — not a sample.
021,244 call sites followed. 32 it could not.97% of what is resolvable — and the rest is counted and named, never reported as clean.
03The links appear, and the folder turns into a map.469 nodes, 4,063 edges, 32 routes — drawn from the code, not from a diagram somebody maintains.
04Select the module everything leans on.lib — where the most of this system goes through.
05Everything that reaches it lights up.8 routes and 2 modules — traced through the call graph, not guessed from names.
06That is the blast radius, as a floor.16 call sites inside it could not be followed, so the honest number is "at least".
expressjs/express· scanned 2026-11-02graph built · 4,063 edges
  1. $npx mlreps scan --publish
  2. reading files141
  3. following call sites1,244
  4. could not follow32
  5. graph edges4,063
  6. routes with a traced path32 of 32

ANY /user/:id{/:op}

DELETE /user/:id

GET /

GET /:sub

GET /403

GET /404

GET /500

GET /api/repos

GET /api/user/:name/repos

GET /api/users

GET /client.js

GET /fail

GET /files/*file

GET /forget

GET /login

GET /logout

GET /middleware

GET /middleware-locals

GET /next

GET /posts

GET /Readme.md

GET /restricted

GET /search/{:query}

GET /user/:id

GET /user/:id/edit

ROUTESYOUR CODEPACKAGESANY /user/:id{/:op}GET /GET /postsGET /user/:idGET /user/:id/editGET /user/:id/view(repository root)exampleslibtestdebugmime-typesproxy-addrrouter

If you change lib

At least 10 things depend on it.

A floor, not a total — 16 call sites inside it could not be followed.

  • routes8
  • modules2
  • not followed16
reaches the selected module6 of 32 routes drawn · 4 of 42 packages · the rest are on the map inside

03 · Then it starts carrying traffic

The same map, with real requests on it.

01One command, and your traces arrive.npx mlreps telemetry writes the instrumentation file, with the key already in it. Everything below is an example of the shape — your own numbers replace it.
02Every span lands on a node you already have.A route, a file, a table — not an anonymous service name.
03One hop is over its threshold. Only that one is marked.A hop nobody measured stays not observed — never a tick on a guess.
04And the cost is inside the request, not beside it.610 of the 842 ms is one database read. That is 73%.
05Which makes the sentence a measurement, not an opinion.Counted over the requests that were actually observed, with that denominator printed.
POST /api/checkout· last 24 h · 4,120 requestsexample, not a measurement

Where the time goes

  • Browsernot observed
  • Next.js server48 ms
  • /api/checkout842 ms
  • postgres · orders610 ms
  • Paymentsnot observed

p95 latency

842 ms

threshold 500 ms

database p95

610 ms

73% of the request

error rate

4.2%

173 of 4,120

throughput

2.9 /s

mean, 24 h

Checkout is slow because one database read takes 73% of the request — and it runs once per order, inside a loop.

OrderService.ts:42 · 14 calls per request · p95 610 ms · over 3,980 of 4,120 observed requests

Open on the mapAsk RadhQuery text is never collected

04 · What should never have been there

The same map finds the risk.

01Rules run over everything that was read.A rule that could not run says so — it is never counted as clean.
02A live key, committed into source.In your history too, so rotating it is part of the fix.
03And a read with nothing checking who is asking.Reachable from two screens — which the graph, not a guess, can tell you.
04Severity is the rule's own. The reach is measured.No invented score about your repository. No grade on your code.

With the GitHub App, our copy of your code is read in a container with no network access, and that copy is removed from our servers when the scan ends. Your repository is never changed. Only the map is kept — never the source, and never a line of it inside a model prompt.

api/checkout/route.ts2 findings
  1. 9export async function POST(req: Request) {
  2. 10  const body = await req.json()
  3. 11  // quick fix before the demo, move later
  4. 12  const key = "sk_live_…"
  5. 13
  6. 14  const order = await db.order.findFirst({
  7. 15    where: { id: body.id } })
  8. 16  return Response.json(order)
  9. 17}
CRITICALcredential-in-source

A live secret key is written into source at route.ts:12.

2 screens reach this route. The key is also in git history.

Do this: rotate it, move it to an environment variable, purge it from history.

HIGHunauthorised-read

This handler reads an order by id with nothing checking that the caller owns it.

Reachable from checkout/page.tsx and admin/orders.

Every rule that could apply ran across everything that was read. The ones that could not run on a language nothing reads yet are listed by name — this says so rather than calling those files clean.

05 · What to do about it

From “this is slow” to a change you can check.

01The problem in the order a person asks it.What · where · why it matters · what to change.
02Before, and after.One query keyed by order id, grouped in memory. Same signature, same tests.
03Fourteen database calls become one.What the rule describes — your next traces decide the real number.
04Then the prompt for your coding agent — editable.Written from the measured facts. You know the constraints; change any word of it.
lib/OrderService.tsexample26 dependants · 318 lines

PROBLEM

A query inside a loop, at line 42

IMPACT

73% of checkout's time

REACH

4 screens, 3 routes

before14 queries
  1. 38for (const o of orders) {
  2. 42  await db.line.findMany(
  3. 43    { where: { orderId: o.id } })
  4. 47}
after1 query
  1. 38const all = await db.line.findMany(
  2. 39  { where: { orderId: { in: ids } } })
  3. 40const by = groupBy(all, l => l.orderId)
  4. 41orders.forEach(o => o.lines = by[o.id])
database calls per request14 1
Prompt for your coding agentCopyEdit

In lib/OrderService.ts, the loop at lines 38–47 calls db.line.findMany once per order (line 42). Replace it with one findMany for every order id in the batch, then group the rows in memory by orderId. Keep the signature and return shape unchanged; order-service.test.ts must still pass.

Written by MLReps from its own analysis, and checked against the measured facts before you saw it.

06 · And you can just ask

Ask in your own words. Get an answer with its workings.

Radh answers from the map — code, structure, dependencies, history, telemetry — and the answer beside this is the real one for a real file in a real repository, computed without calling a model at all.

It cannot invent anything. The map answers and the model only words it — so where the evidence is missing, it tells you what is missing instead of filling the gap.

What is lib/express.js connected to?

R

Changing lib/express.js could reach 117 files — 97 of them directly.

  • module: lib
  • feature: none — shared infrastructure
  • symbols recorded: 11
  • uses: 3 files
  • used by: 97 files
  • imports: 4 packages
  • 78% of the call sites in lib resolved, so 16 calls in this module were not followed. Anything reached only through those is not listed here.

A lower bound — some calls resolve for no static tool.

Answered without calling a model. Yours is answered the same way.

Ask it about your code

What you end up with

Not a dashboard you check. A loop you work in.

Every screen hands you to the next with the subject already carried over. You never retype a file name to ask about it.

Understand

The architecture, and which file owns which feature.

See

Latency, errors and queries on those same nodes.

Explain

Why, with the decomposition that proves it.

Fix

A recommendation, and the prompt that carries it out.

Verify

The next scan and the next traces say whether it worked.

Access, in plain words

You decide what it can see. One repository at a time.

Not an “enterprise-grade security” sentence. The actual boundary, on the page, before you install anything.

What it can do

  • Read one repository you approved, read-only
  • Publish the map to your own organisation
  • Receive the telemetry you choose to send

The approval screen names the machine, the folder, the repository and the organisation — before anything is created.

What it never does

  • Write to your repository or open a pull request
  • Put your source into a model prompt
  • Train anything on your code
  • Show one customer’s data to another
  • Give you a per-developer view of who wrote what

What telemetry never carries

  • Log lines and query text
  • Span names, URLs, your trace ids
  • Exception messages and stack traces
  • Host names, device names, mount points

Aggregates 14 days · samples 3 days · your source not kept at all.

Every machine, every repository — one list, with a Revoke beside each

your dashboard
  • dev-mbp-14 · ~/code/storefront · 20 Oct

    may act on acme/storefront, acme/admin

    Revoke
  • ci-runner-7 · /home/runner/work · 14 Oct

    may act on acme/storefront · 1 taken back

    Revoke

Where this stands today

Six of the eight rungs are built. Two are begun.

You should read that here, not find it out in week two.

  1. Observe

    built

  2. Understand

    built

  3. Verify

    built

  4. Predict

    built

  5. Explain

    built

  6. Fix

    built

  7. Optimize

    begun

  8. Learn

    begun

What it refuses to say

The reason to believe the numbers is the ones it will not print

  • A score it did not measure.

    Three of the six indices are uncalibrated, and they say so instead of printing a number.

  • A map of a codebase it could not read.

    Below 70% resolution no picture is drawn at all; between 70 and 90 it is drawn with its gaps named.

  • That a change is safe.

    An empty blast radius means we found nothing, never that nothing is affected.

  • Anything about a person.

    No per-developer score, no leaderboard, and no column anywhere for who dismissed a finding — and debt is hours, never a letter grade.

Start

Connect your repository. See what it finds.

One repository, one command, about two minutes. It runs on your machine first, and you approve that repository by name before anything is published.

Free — there is no paid plan, and nothing is held back waiting for one.

Or read what it finds: your real architecture, traced from the code · the query a loop runs once per item · what fast-written code leaves behind

Connect a repository What it sees