Skip to content
PatchArc

Monorepo scopes

How PatchArc maps a change to the packages it touched, the ten workspace layouts it detects, and how to declare scopes yourself.

validated against patcharc 0.2.0 · 2026-08-22

A scope is a unit of ownership inside a repository: a package, a service, a module. The review's "Affected scopes" section groups every changed file by scope, with additions, deletions, and a severity band, so a reviewer reads "the auth service and the shared client" instead of a flat list of 40 paths.

Automatic detection

Run patcharc scopes in any repository:

$ patcharc scopes
Scopes (4):
  - scope_apps_web [package] Web
      apps/web/**
  - scope_packages_client [package] Client
      packages/client/**
  - scope_packages_server [package] Server
      packages/server/**
  - scope_tools [package] Tools
      tools/**

Detection runs in this order and stops collecting from a detector only if it errors; results are merged:

DetectorReads
pnpmpnpm-workspace.yaml packages:
npm / yarnpackage.json workspaces (array or workspaces.packages)
Nxnx.json plus every project.json
Rushrush.json projects[]
Bazelevery BUILD / BUILD.bazel
Gogo.work use entries
CargoCargo.toml [workspace] members
Mavenevery non-root pom.xml
Gradlesettings.gradle(.kts) plus every build.gradle(.kts)
.NETevery *.csproj

That is ten working detectors. A turbo.json is recognised but contributes no scopes on its own (Turborepo projects are usually pnpm or npm workspaces, which are detected). There is no Python detector in 0.2.0.

Scope ids are derived from the path (packages/server becomes scope_packages_server); titles are the last path segment, title-cased.

Declaring scopes

Explicit scopes in .patcharc/config.yaml always win over detection:

scopes:
  - id: auth
    title: Auth service
    kind: service
    paths: ["services/auth/**"]
    owners: ["@platform"]
    depends_on: ["shared"]
  - id: shared
    title: Shared client
    kind: package
    paths: ["packages/client/**"]
cross_cutting:
  - ".github/**"
  - "pnpm-lock.yaml"
  • id must match [a-z0-9_-]+ and be unique; title and at least one paths glob are required.
  • Globs support **, *, and ?.
  • cross_cutting globs are matched first; files that match land in a cross_cutting bucket rather than any scope.
  • depends_on is the only source of edges in the scope graph.

patcharc scopes:configure prints a pointer to the config file in 0.2.0; edit the YAML directly.

Classification order

For each changed file: graph cross-cutting globs, then config cross_cutting, then scope paths in declaration order, then unclassified. Unclassified files appear in the review under their own heading so nothing is silently dropped.

The dependency graph

$ patcharc scopes:graph
digraph scopes {
  "auth";
  "shared";
  "auth" -> "shared";
}

Edges come only from depends_on. Detectors do not infer dependencies in 0.2.0, so on an unconfigured repository the graph has nodes and no edges. Pipe the output to dot -Tsvg to draw it.

How scopes appear in the review

Each scope summary carries the commits and files that touched it (computed per commit with git diff-tree), additions and deletions, and a severity band: major at 500 or more changed lines, moderate at 100, minor below that, none for untouched scopes (which are omitted). See The six review sections.