prometeu-studio/discussion/lessons/DSC-0064-multi-frontend-architectural-tests/LSN-0066-archunit-guards-java-multi-frontend-boundaries.md
bQUARKz e809ce63d3
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 62.10% (17811/28682) * Branch Coverage: 52.81% (6878/13025) * Lines of Code: 28682 * Cyclomatic Complexity: 11494 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 654
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
implements PLN-0129
Add ArchUnit guards for Java multi-frontend boundaries in a test-only
module. Identify PBS by frontend-module origin, allow only the registry
as composition root, and lock the wording in specs 19, 20, and 22.

Housekeep DSC-0064 with LSN-0066.
2026-09-19 03:32:43 +01:00

152 lines
6.3 KiB
Markdown

---
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`