--- id: LSN-0059 ticket: multi-frontend-frontend-backend-contract title: Common IRBackend handoff and backend guardrails created: 2026-07-15 tags: [compiler, compiler-general, compiler-pbs, ir, backend, multi-frontend] --- # 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: 1. rewrote the general backend spec so `IRBackend -> IRVM` is explicitly compiler-general; 2. rewrote the PBS lowering spec so PBS emits the common handoff instead of owning backend behavior; 3. updated the conformance matrix with DEC-0043 guardrail rows; 4. added a direct backend test that manually constructs common `IRBackend` and lowers it without PBS parser or frontend services; 5. added an architectural test preventing common backend imports from `p.studio.compiler.pbs`; 6. added a reflection-based contract test preventing public `IRBackend` model types from exposing PBS packages; 7. renamed the common lifecycle helper from `validatePbsLifecycleStructure` to `validateCommonLifecycleStructure`. ## Patterns and Algorithms ### Boundary consolidation before abstraction When an existing boundary is already technically sound, prefer consolidation over redesign: 1. document the current contract as normative; 2. identify only concrete leaks; 3. add tests that would fail if the boundary regresses; 4. 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: 1. source/package guard: common backend source must not import language frontend packages; 2. 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 `IRBackend` for 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 `IRBackend` construction tests. ## Takeaways - `IRBackend` is the common executable handoff. - PBS owns PBS-to-`IRBackend` lowering; compiler-general owns `IRBackend -> IRVM`. - Common backend code must not depend on `p.studio.compiler.pbs`. - Public `IRBackend` contract 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.md` - `docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md` - `docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md` - `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java` - `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/specs/CommonBackendArchitectureTest.java` - `prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java`