5.7 KiB
| id | ticket | title | created | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| LSN-0059 | multi-frontend-frontend-backend-contract | Common IRBackend handoff and backend guardrails | 2026-07-15 |
|
Common IRBackend handoff and backend guardrails
Context
The multi-frontend work needed a clear boundary between language-specific frontends and common backend stages.
The existing code was already close to the desired architecture: PBS parsing, AST construction, and semantic analysis lowered into IRBackend, while the backend consumed IRBackend and lowered it into IRVM. The risk was not a known broken dependency. The risk was that common backend behavior still looked PBS-owned in specs, names, and missing guardrails, which could invite future coupling when another frontend is introduced.
Key Decisions
IRBackend is the common executable handoff
What: IRBackend remains the normative frontend-to-backend executable handoff. Frontends emit it; common backend stages consume it.
Why: The existing model already provides a language-neutral executable contract with modules, callable identities, executable functions, instruction metadata, globals, synthetic functions, host calls, intrinsics, spans, and capabilities. Renaming or redesigning it without a concrete leak would add churn without improving the boundary.
Trade-offs: Keeping the name IRBackend means the architecture relies on documentation and tests to communicate its common role. That is acceptable because the name is already established and the guardrails now enforce the boundary.
PBS owns lowering into IRBackend, not backend consumption of it
What: PBS specs describe PBS admission and translation into IRBackend. Compiler-general specs describe the common IRBackend -> IRVM contract and backend obligations.
Why: PBS is currently the only rich frontend producing executable IRBackend, so many examples naturally come from PBS. Specs must still separate source-language obligations from backend obligations so future frontends do not inherit PBS-specific rules by accident.
Trade-offs: Some PBS-facing examples remain useful, especially around lifecycle wrappers and asset metadata. They must be phrased as current frontend sources of a common backend obligation, not as backend ownership by PBS.
Implementation Result
The completed work:
- rewrote the general backend spec so
IRBackend -> IRVMis explicitly compiler-general; - rewrote the PBS lowering spec so PBS emits the common handoff instead of owning backend behavior;
- updated the conformance matrix with DEC-0043 guardrail rows;
- added a direct backend test that manually constructs common
IRBackendand lowers it without PBS parser or frontend services; - added an architectural test preventing common backend imports from
p.studio.compiler.pbs; - added a reflection-based contract test preventing public
IRBackendmodel types from exposing PBS packages; - renamed the common lifecycle helper from
validatePbsLifecycleStructuretovalidateCommonLifecycleStructure.
Patterns and Algorithms
Boundary consolidation before abstraction
When an existing boundary is already technically sound, prefer consolidation over redesign:
- document the current contract as normative;
- identify only concrete leaks;
- add tests that would fail if the boundary regresses;
- avoid renames or model extraction until there is evidence of real coupling.
This keeps multi-frontend preparation focused on preventing regressions rather than inventing abstractions prematurely.
Guardrails for common backend neutrality
A useful backend neutrality guardrail has two layers:
- source/package guard: common backend source must not import language frontend packages;
- public-contract guard: common handoff types must not expose language-owned types through fields, constructors, methods, or record components.
The package guard catches implementation coupling. The reflection guard catches API coupling.
Direct handoff construction test
A backend test should be able to construct executable IRBackend manually using only common model types and lower it through LowerToIRVMService.
That test proves the backend consumes the contract, not a specific frontend pipeline.
Pitfalls
- Do not treat the presence of PBS examples as proof that the backend is PBS-owned.
- Do not move PBS source rules into compiler-general specs.
- Do not let lifecycle terms such as published wrapper, boot guard, or frame root hide whether the obligation is common or PBS-specific.
- Do not rename
IRBackendfor aesthetics when the real problem is missing ownership language and missing guardrails. - Do not use a future synthetic test frontend as a substitute for simple direct
IRBackendconstruction tests.
Takeaways
IRBackendis the common executable handoff.- PBS owns PBS-to-
IRBackendlowering; compiler-general ownsIRBackend -> IRVM. - Common backend code must not depend on
p.studio.compiler.pbs. - Public
IRBackendcontract types must not expose PBS AST, token, parser, semantic, or editorial types. - Multi-frontend preparation should first lock existing good boundaries before introducing new abstractions.
References
docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.mddocs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.mddocs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.mdprometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.javaprometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/specs/CommonBackendArchitectureTest.javaprometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java