Skip to content

v4.188.0

@jessegall jessegall tagged this 25 Jul 06:27
NamespaceCycleDetector: two namespaces that reference each other. Neither can be read,
tested or moved without the other, so the pair is one unit wearing two names. Unlike a
declared layer breach it needs no config to be wrong — whatever stack a project meant,
nothing in a cycle can be at the bottom of it — so it fires out of the box.

Calibration reshaped it twice, and both changes are the detector:

- A namespace NESTED in another is part of it, not a peer. `App\Caching` leaning on its
  own `App\Caching\Concerns` was flagged — a false positive, and the same freedom two
  classes side by side in one namespace already have. Those edges also welded whole
  trees into single components (653 findings here, 803 in laravel-data).
- Report the MUTUAL PAIR, not the strongly-connected component. A 130-namespace tangle
  is a cycle too, but no single arrow in it is the mistake, so naming all 1483 of them
  named nothing. A pair is where a person can decide. Findings dropped to 15/20/78 on
  three real trees, each one arrow someone can cut.

Of the two directions it reports the THINNER one: fewest places to change, and after
the cut the pair points one way again.

New shared analysis: Ast\Support\NamespaceGraph — the namespace dependency graph, built
once per codebase, with the mutual pairs and a dependency order. The `layers` command
reads the same graph.

Verified by eye on real hits: App\Models implement App\Contracts while
BackupServiceInterface returns a Model back — the flagged arrow is the accidental one.

The Shop fixture grew a Tokens layer so the layer demo no longer forms a pair of its
own, and the Enums <-> Shipping cycle already sitting in it is now marked.
Assets 2
Loading