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.
6.4 KiB
| id | ticket | title | created | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| LSN-0065 | multi-frontend-synthetic-test-frontend | Synthetic test frontend proves common pipeline neutrality | 2026-09-19 |
|
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):
languageIdissynth.- The provider and
FrontendPhaseServicelive only inprometeu-build-pipelinetest sources. languageService()is empty. There is no parser, grammar, semantic presentation, or stdlib catalog.sourceRootsissrc.allowedExtensionsmust not collide withpbs/barrel. The fixture must not create a source file of that extension. An emptysrc/directory must still exist becauseWireProjectsPhaseresolvestoRealPath().- The compiler ignores source text and emits one module, one
framebody that is a singleRET, and anIRLifecycleDeclarationwith exactly oneFRAME_ROOTwhose origin span is filled. - The handoff must not declare
FILE_INIT_FRAGMENTorPROJECT_INIT, and must not pre-materialize wrappers, module-init, orBOOT_GUARD. The common assembler produces__prometeu.lifecycle.frame_wrapper$mNand 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.SyntheticFrontendProviderp.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
languageIdissynth, notpbs; - 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/.barrelsources 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 emptysrc/, unregister; FRAME_ROOTwithFileId(0)and a non-none span, even with no source file inFileTable;- assert the synthetic compiler was invoked instead of importing
PBSFrontendPhaseServicefrom build-pipeline tests.
Bad:
- omitting
languagein the manifest so discovery silently uses PBS; - creating
main.dummyand calling that a second frontend; - putting the provider under
prometeu-compiler/frontends/; - leaving
synthregistered after the test so later tests see two providers; - using this pipeline test as a substitute for assembler or IRVM unit tests.
Pitfalls
WireProjectsPhasestill needssrc/on disk. Empty is not the same as missing.- Origin span
Span.none()is rejected by the assembler. Use a realFileIdeven 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 showsynthas a new-project language.- Direct
IRBackendconstruction 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.mddocs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.mdprometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.javaprometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/synth/SyntheticFrontendProvider.javaprometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/SyntheticFrontendPipelineTest.java