prometeu-studio/discussion/lessons/DSC-0054-multi-frontend-provider-contract/LSN-0055-static-frontend-providers-before-plugin-architecture.md
bQUARKz 6fdb4828c3
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.49% (17397/28294) * Branch Coverage: 52.32% (6724/12852) * Lines of Code: 28294 * Cyclomatic Complexity: 11339 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 608
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
housekeep DSC-0054
2026-07-15 08:37:22 +01:00

4.2 KiB

id ticket title created tags
LSN-0055 multi-frontend-provider-contract Static frontend providers before plugin architecture 2026-07-15
compiler
compiler-general
studio
frontend
registry
multi-frontend

Context

Prometeu needed a multi-frontend boundary without turning the compiler or Studio into a dynamic plugin platform. Before this work, shared code could ask the registry for frontend specs or phase services, and PBS was registered directly as the default language. That worked for a single frontend, but it made the next frontend harder because common code could still depend on PBS-specific construction details.

The durable ownership rule is now: common compiler and Studio code select a frontend by languageId, then consume a provider contract. PBS remains the first and only real provider, but it is no longer the shape of the shared abstraction.

Key Decisions

FrontendProvider is the common registration unit

What: Each registered frontend is represented by a FrontendProvider with:

  • specification(), for the static frontend contract and languageId;
  • compiler(), for compiler-facing frontend execution;
  • languageService(), as an optional editor-facing capability.

Why: This groups the frontend identity, compiler entrypoint, and optional tooling capability without forcing every frontend to implement Studio/editor services.

Trade-offs: The provider is intentionally small. It does not solve dynamic discovery, plugin installation, external loading, or all future language-service APIs. Those are separate architectural decisions.

Registration is explicit

What: Frontend registration is static and composition-root-owned. The application composition path bootstraps the default provider set, and the registry resolves providers by languageId.

Why: Explicit registration keeps dependencies visible and testable. It lets composition know about PBS while preventing common compiler and Studio consumers from directly constructing PBS services.

Trade-offs: Adding a new in-repository frontend still requires a code change in composition. That is acceptable because this decision optimizes for repository-internal multi-frontend readiness, not external plugin extensibility.

PBS is a provider, not the shared model

What: PBS is wrapped by PBSFrontendProvider. Shared build and Studio consumers obtain frontend behavior through FrontendRegistryService.require(languageId) or provider-derived listings.

Why: PBS remains the correctness baseline, but it must not become the exclusive owner of compiler pipeline semantics.

Trade-offs: PBS-specific tests may still instantiate PBSFrontendPhaseService directly because they test PBS internals. Common modules should not.

Patterns and Algorithms

Use this path for common compiler execution:

  1. Resolve the project language from the manifest or default provider.
  2. Resolve FrontendProvider by languageId.
  3. Use provider.specification() for static metadata.
  4. Use provider.compiler() for frontend compilation.
  5. Use provider.languageService() only when a tooling consumer needs optional editor services.

Use this path for Studio language catalogs:

  1. List providers from the registry.
  2. Derive templates from provider.specification().
  3. Do not hardcode PBS templates in Studio catalog code.

Pitfalls

  • Do not add reflection, classpath scanning, external JAR loading, or runtime plugin installation to solve repository-internal frontend registration.
  • Do not make languageService() mandatory. Compilation and editor assistance are separate capabilities.
  • Do not instantiate PBSFrontendPhaseService from common build, dependency, Studio, or app code.
  • Do not use PBS as a fallback for unknown languageId. Unknown languages must fail explicitly.
  • Do not move frontend semantic ownership into Studio just because Studio consumes provider metadata.

Takeaways

  • Static providers are enough to prepare for multiple in-repository frontends.
  • The registry boundary is provider-based, not PBS-based.
  • Composition may know which frontends exist; common consumers should only know FrontendProvider.
  • Optional editor capabilities keep compiler readiness independent from Studio feature depth.