--- id: LSN-0066 ticket: multi-frontend-architectural-tests title: ArchUnit guards Java multi-frontend boundaries created: 2026-09-19 tags: [compiler, compiler-general, studio, frontend, architecture, tests, multi-frontend] --- # ArchUnit guards Java multi-frontend boundaries ## Original Problem The multi-frontend boundaries already existed in specs and lessons: common code selects a frontend by `languageId`, `IRBackend` is language-neutral, lifecycle assembly is common, and PBS is one provider. The automated protection did not match that model. The old guards walked Java source and searched for substrings: - `import p.studio.compiler.pbs` - `new PBSFrontendPhaseService(` - `new PbsEditorialSupportService(` Those checks missed field, signature, and constructor coupling. They also treated `p.studio.compiler.pbs` as the whole PBS surface. Facades such as `PBSFrontendProvider`, `PBSDefinitions`, and `PBSFrontendPhaseService` live in the PBS module but not in that package. `prometeu-app` and `prometeu-lsp-v1` declared an `implementation` dependency on PBS without importing any PBS type. ## Consolidated Decision Java multi-frontend boundaries are enforced by **ArchUnit + JUnit 5**, not by source substring search, except for the `__pbs.` literal scan that bytecode cannot see. Durable locks from DEC-0050: - ArchUnit is test-only, pinned in `gradle/libs.versions.toml`. - Rules live in the test-only Gradle project `:prometeu-architecture-tests`. That project has no production sources. It is not a plugin loader. - A PBS type is a class whose bytecode originates from `:prometeu-compiler:frontends:prometeu-frontend-pbs`. Package `p.studio.compiler.pbs` is not the complete boundary. Name prefixes `PBS*` / `Pbs*` are not the identity. - Production may depend on PBS types only in: - the PBS module itself; - `prometeu-frontend-registry` / `FrontendRegistryService`, and only on `PBSFrontendProvider` and `PBSDefinitions`. - `AppContainer` is not a PBS composition root. It may call `FrontendRegistryService.bootstrapDefaults()`. It must not instantiate PBS types. - `synth` is not in the production allowlist and must not be registered in `bootstrapDefaults()`. - Runtime, PVM, and the Rust repository stay out of this discussion (LSN-0061). - `IRBackendExecutableContractTest` remains the public-contract shape audit. ArchUnit covers module-origin dependence. Do not migrate PBS facades into another package just to make the test easier. Do not ban the word `PBS` in docs, fixtures, or comments. ## Final Implementation `:prometeu-architecture-tests` imports production classes under `p.studio` with `ImportOption.DoNotIncludeTests` and applies three ArchUnit rules: 1. classes outside the PBS module and the registry must not depend on PBS-module types; 2. registry production may depend only on `PBSFrontendProvider` and `PBSDefinitions` from that module; 3. frontend-api `p.studio.compiler.models` must not depend on PBS-module types. A separate JUnit test walks `build.gradle.kts` files and allows the PBS project dependency only for: - `:prometeu-compiler:prometeu-frontend-registry` as `implementation`/`api`; - `:prometeu-architecture-tests` as `testImplementation` (needed to classify class origin). The `__pbs.` source scan moved to `CommonBackendPbsPrefixScanTest` and still covers `backend/`, `lifecycle/`, and `workspaces/stages/`. Gradle graph cleanup: - `prometeu-app` and `prometeu-lsp-v1` no longer `implementation` PBS. Runtime classloading of `PBSFrontendProvider` still comes through the registry's implementation dependency. - `prometeu-studio` no longer `testImplementation` PBS. No studio test compiled against PBS types. Source-walk import/constructor tests were deleted. `FrontendProviderBoundaryTest` keeps only `compileOnlyFrontendProviderMayOmitLanguageService`. Specs 19 ยง11 and 20 now say common/platform code must not depend on types from the PBS frontend module. Matrix rows G19-11.6, G20-4.1.2, and G20-4.3.3 point at the ArchUnit rules and the `__pbs.` scan. ## Examples Good: - ArchUnit failure names the class that depends on a PBS-module type, including a facade in package `p.studio.compiler`. - Registry `bootstrapDefaults()` constructs `PBSFrontendProvider`. That is the allowed composition root. - App calls `FrontendRegistryService.bootstrapDefaults()` and has no PBS project dependency. Bad: - Searching source for `import p.studio.compiler.pbs` and calling the boundary protected. - Treating `AppContainer` as the place that may `new PBSFrontendProvider()`. - Identifying PBS by class name prefix, so a future `PbsLike` helper in common code is banned or a PBS facade is missed. - Adding ArchUnit to `java-common-conventions` for every module. ## Pitfalls - Same Java package does not mean same Gradle module. `FrontendRegistryService` and `PBSFrontendProvider` both sit in `p.studio.compiler`. Origin is the module path or JAR name. - `@AnalyzeClasses` classes must not mix `@ArchTest` and Jupiter `@Test` on the same class. The ArchUnit engine owns `@ArchTest`. - `DoNotIncludeTests` is required. The architecture-test sources themselves reference PBS types in order to name the allowlist. - Removing PBS from app/lsp compile classpath is safe only because registry still `implementation`s PBS, which is runtime-transitive. - `testImplementation` of PBS on studio is not a production leak, but if no studio test needs PBS types it is still a lying graph. The allowlist test rejects it. - Do not point ArchUnit at the Rust runtime. `__pbs.` in Java source is a string contract, not a type dependency. ## References - Decision: `DEC-0050` - Plan: `PLN-0129` - Related lessons: `LSN-0055`, `LSN-0059`, `LSN-0062`, `LSN-0063`, `LSN-0061`, `LSN-0065` - `docs/specs/compiler/19. Verification and Safety Checks Specification.md` - `docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md` - `docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md` - `prometeu-architecture-tests/src/test/java/p/studio/architecture/PbsModuleBoundaryArchTest.java` - `prometeu-architecture-tests/src/test/java/p/studio/architecture/PbsGradleDependencyAllowlistTest.java` - `prometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.java` - `prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java`