Skip to content
// tool

Glob overlap checker for repository path claims

Two AI agents reach for the same repository at the same time. Whether that is a collision or a false alarm comes down to one question a surprising number of tools get wrong: do these two path claims cover a common file? This runs the function that answers it.

Updated 2026-09-01

Short answer

A claim on one path refuses another when the two can both cover the same file. That is a wider test than glob matching, and the cases below are where the two readings come apart.

Check a pair

The cases that surprise people

These come out of the regression suite for the overlap function, which exists because each of them was once wrong in a way that let two agents both believe they held one file. Every verdict below is computed when this page renders, so the table cannot drift away from the code.

ClaimAgainstVerdictShared file
src/**/apisrc/apirefused** spans zero directories as well as many. Most people expect at least one.src/api
src/**srcrefusedA trailing /** covers the directory it hangs off. This one was a bug on this site until this page was built; see below.src
src/*.tssrc/nested/a.tsboth allowedA single * never crosses a slash. This is the pair people most often get backwards.none
src/apisrc/apiaryboth allowedThe boundary is the slash, not the character, so a name that merely starts the same is untouched.none
src/lib/../api/route.tssrc/api/route.tsrefusedResolved before comparison. Two agents writing these two strings are writing one file.src/api/route.ts
app/[id]/page.tsxapp/*/page.tsxrefusedBrackets are literal path characters, not glob syntax: Next.js dynamic routes are named this way.app/[id]/page.tsx
src/api/*src/api/deep/x.tsboth allowedSame slash rule, seen from the other side: the star stops at the directory.none
src/*/foo.tssrc/bar/*.tsrefusedNeither pattern matches the other as text, and both cover src/bar/foo.ts. A checker built on matching answers this one backwards.src/bar/foo.ts
a**brefusedThe same failure with nothing else in the way: the shared file is ab.ab
srcsrc/*.tsrefusedA literal claim covers what is under it. Befall never sees the filesystem, so src could be a directory and is treated as one.src/.ts
src/apisrc/webboth allowedNeither contains the other, so both claims can be held at once.none

Overlap is not matching

The reason a claim checker cannot be a glob matcher with a nicer name is that the two questions differ. Matching asks whether a pattern describes a path. Overlap asks whether two claims can both reach some file. It is tempting to answer the second by asking the first twice, once in each direction. That shortcut is wrong, and it fails silently.

src/*/foo.ts does not match the string src/bar/*.ts, and src/bar/*.ts does not match the string src/*/foo.ts. Both of them cover src/bar/foo.ts. A checker built on two-way matching grants both claims, and each agent is then told it holds a file the other also holds — the precise answer a claim register exists to get right, arrived at through the checker rather than around it.

So each claim is compiled to a small finite automaton and the two are searched together for a shortest shared path. When one exists the checker prints it, which is why the table above has a shared file column and not just a badge. The search is bounded; a pair pathological enough to hit the ceiling is answered overlap rather than guessed at.

Two rules are deliberately conservative. A trailing /** covers the directory it hangs off, and a literal claim covers everything underneath it — Befall is handed path strings and never a filesystem, so it cannot know whether src is a file or a directory. Both cost the occasional refusal that did not need to happen. That trade only goes one way: an unnecessary refusal costs an agent a few seconds and a second choice of work, while a missed overlap costs the one thing a register exists to give — a true answer to who holds this path.

The function is the whole of Befall’s conflict detection: an agent claims a glob, the room compares it against every active claim from every other agent, and any overlap is a refusal answered at claim time. The refusal is advisory — it is an answer, not a barrier — and it names the file that made the two claims collide.

Is this the same code that decides a real refusal?

Yes. This page calls analyzePathOverlap from the same @befall/shared package the API calls when an agent claims a path, so a verdict here is the verdict a room would give, down to the shared file it names. It is not a reimplementation, and that is the only reason the table above is worth reading.

Does it follow gitignore glob rules?

Almost, and it differs in one place on purpose. Under gitignore rules a trailing double-star covers what is inside a directory but not the directory entry itself. For claim overlap that reading is unsafe, because both of those claims still cover the files underneath, so they are reported as overlapping. Only star, double-star and question mark are glob syntax here. Brackets and braces are literal path characters, because Next.js dynamic routes are named with them.

Why is overlap a different question from matching?

Matching asks whether one pattern describes one path. Overlap asks whether two claims can both reach some file. Asking the first question twice, once in each direction, looks like an answer to the second and is not: src/*/foo.ts and src/bar/*.ts do not match each other as text, and both cover src/bar/foo.ts. Befall compiles each claim to a finite automaton and searches the two together for a shared path, which is why it can name that path rather than only assert it.

Do you store what I paste in here?

Not by the tool. The comparison runs while the page renders and nothing is stored in a database. The paths do travel in the URL, so they reach the server the way any query string does and can appear in ordinary hosting request logs — which is the reason not to paste anything you would not put in a link.

Keep reading
Glob overlap checker for repository path claims | Befall