prometeu-studio/discussion/lessons/DSC-0063-multi-frontend-synthetic-test-frontend/LSN-0065-synthetic-test-frontend-proves-pipeline-neutrality.md
bQUARKz 79a682b8ec
Some checks are pending
Intrepid/Prometeu/Studio/pipeline/pr-master Build started...
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: 653
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
implements PLN-0128
Add a test-only synth FrontendProvider that traverses compile() to
verified bytecode without PBS. Expose register/unregister on the static
registry and lock the obligation in specs 23 and 22.

Housekeep DSC-0063 with LSN-0065.
2026-09-19 03:03:37 +01:00

149 lines
6.4 KiB
Markdown

---
id: LSN-0065
ticket: multi-frontend-synthetic-test-frontend
title: Synthetic test frontend proves common pipeline neutrality
created: 2026-09-19
tags: [compiler, compiler-general, frontend, tests, backend, multi-frontend]
---
# Synthetic test frontend proves common pipeline neutrality
## Original Problem
The common pipeline already had `FrontendProvider`, `IRBackend` as the
executable handoff, common lifecycle assembly, and `IRBackend -> IRVM -> PBX`.
Direct construction tests proved the backend could lower a handmade
`IRBackend`. Import guards proved common backend code did not depend on PBS
packages.
That was not enough. Nothing proved that a second provider, resolved by
`languageId`, could enter through `DiscoverPhase` and
`FrontendPhasePipelineStage`, assemble lifecycle, and finish
`BuilderPipelineService.compile()` at verified bytecode without instantiating
PBS, reading `.pbs` files, or loading PBS stdlib.
`CompileOnlyFrontendProvider` already existed as a local test double. It was
not registered, returned an empty `IRBackend`, and never called the pipeline.
It did not close this gap.
## Consolidated Decision
Pipeline neutrality is proved by a **test-only** compile-only
`FrontendProvider`, not by a second product language and not by injecting IR
into compile stages.
Locked shape (former Option A):
- `languageId` is `synth`.
- The provider and `FrontendPhaseService` live only in
`prometeu-build-pipeline` test sources.
- `languageService()` is empty. There is no parser, grammar, semantic
presentation, or stdlib catalog.
- `sourceRoots` is `src`. `allowedExtensions` must not collide with `pbs` /
`barrel`. The fixture must **not** create a source file of that extension.
An empty `src/` directory must still exist because `WireProjectsPhase`
resolves `toRealPath()`.
- The compiler ignores source text and emits one module, one `frame` body
that is a single `RET`, and an `IRLifecycleDeclaration` with exactly one
`FRAME_ROOT` whose origin span is filled.
- The handoff must not declare `FILE_INIT_FRAGMENT` or `PROJECT_INIT`, and
must not pre-materialize wrappers, module-init, or `BOOT_GUARD`. The common
assembler produces `__prometeu.lifecycle.frame_wrapper$mN` and the boot
guard.
- The IR must not contain host bindings, capabilities, intrinsics, PBS types,
PBS stdlib, or `__pbs.` names.
The acceptance path is `compile()` through `VerifyBytecode`. Writing
`program.pbx` is not the criterion. `build()` only adds disk I/O.
Registration stays on the existing static `FrontendRegistryService`.
`register` and `unregister` are public. `unregister` refuses to remove the
default PBS provider and is a no-op for unknown ids. Tests register `synth`
for the duration of the run and unregister in `try/finally`.
`bootstrapDefaults()`, `AppContainer`, and the Studio language catalog must
not register `synth`.
Do not inject a `Registry` into the pipeline, add a `frontends/synth` module,
scan the classpath, or treat a dummy on-disk extension as a language. Do not
replace `LifecycleAssemblerServiceTest`, `LowerToIRVMServiceTest`, or
`IRBackendExecutableContractTest` with this fixture.
A blank `prometeu.json` `language` field still defaults to PBS. The synthetic
project must set `language: synth` explicitly.
## Final Implementation
`FrontendRegistryService.register(FrontendProvider)` is the explicit
registration API. `unregister(String languageId)` removes a non-PBS provider
or no-ops if the id is absent. Duplicate `register` still fails.
`bootstrapDefaults()` still registers only PBS.
The test fixture is:
- `p.studio.compiler.synth.SyntheticFrontendProvider`
- `p.studio.compiler.synth.SyntheticFrontendPhaseService`
`SyntheticFrontendPipelineTest` writes a temporary project
(`language: synth`, `target: Game`, numeric `stdlib`, empty `src/`),
registers the provider, calls `BuilderPipelineService.compile()`, and
asserts:
- the synthetic compiler ran;
- resolved `languageId` is `synth`, not `pbs`;
- diagnostics have no errors;
- in-memory bytecode is non-empty and verified;
- assembled entrypoint is `__prometeu.lifecycle.frame_wrapper$m0`;
- a hidden boot guard is present;
- no `__pbs.` names;
- no `.pbs` / `.barrel` sources were required;
- after `unregister`, `listProviders()` is PBS-only.
Spec 23 section 8.3 records that a compile-only registered provider is a
valid pipeline participant and that the common pipeline must not require PBS
types, PBS sources, or PBS stdlib to emit verified bytecode. Spec 22 row
`G23-8.3` binds that obligation to the synthetic pipeline test.
PBS production code, Studio, and `AppContainer` were not changed.
## Examples
Good:
- register `synth`, compile a temp project with empty `src/`, unregister;
- `FRAME_ROOT` with `FileId(0)` and a non-none span, even with no source file
in `FileTable`;
- assert the synthetic compiler was invoked instead of importing
`PBSFrontendPhaseService` from build-pipeline tests.
Bad:
- omitting `language` in the manifest so discovery silently uses PBS;
- creating `main.dummy` and calling that a second frontend;
- putting the provider under `prometeu-compiler/frontends/`;
- leaving `synth` registered after the test so later tests see two providers;
- using this pipeline test as a substitute for assembler or IRVM unit tests.
## Pitfalls
- `WireProjectsPhase` still needs `src/` on disk. Empty is not the same as
missing.
- Origin span `Span.none()` is rejected by the assembler. Use a real
`FileId` even when no file was loaded.
- `unregister("pbs")` must fail. A cleanup path that blindly unregisters the
resolved language would delete the product default.
- `listProviders()` feeds the Studio catalog. A leaked test registration
would show `synth` as a new-project language.
- Direct `IRBackend` construction tests still own backend-only proofs. The
synthetic frontend owns the registry-plus-pipeline proof. Keep both.
## References
- Decision: `DEC-0049`
- Plan: `PLN-0128`
- Related lessons: `LSN-0055`, `LSN-0056`, `LSN-0059`, `LSN-0062`, `LSN-0061`
- `docs/specs/compiler/23. Compiler Pipeline Entry Points Specification.md`
- `docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md`
- `prometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.java`
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/synth/SyntheticFrontendProvider.java`
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/SyntheticFrontendPipelineTest.java`