Module A reaches into module B's private fields or undocumented behavior; the agent reasoning about A must also load B's internals to make any change.
Cooperation flows through a narrow named interface the agent can read once; A's reasoning context excludes B's implementation details.
Smellier version
class A { _data; }class B {read(a) {return a._data.value;}}
Fresher version
class A { value() { return this._data.value; } }class B { read(a) { return a.value(); } }
Refactoring one module silently breaks the other in ways the type system doesn't catch; the agent must trace cross-module assumptions on every edit.
Defining a real public interface adds a contract the agent must respect at both ends; until the interface stabilizes, every change forces synchronized edits across both modules.
Each module's public surface is the only contract callers depend on; the agent reads one module to predict behavior instead of loading both modules together to verify the unwritten coupling still holds.
Erecting elaborate public APIs between modules that genuinely belong together creates a fake boundary the agent must navigate at every interaction with no isolation gain.