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
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.
149 lines
6.4 KiB
Markdown
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`
|