This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-graalvm-distro.git

commit daac54d3d8783a748672ce9af94097bf609917b4
Author: Wu Sheng <[email protected]>
AuthorDate: Wed Feb 18 22:04:20 2026 +0800

    Update PLAN.md and OAL-IMMIGRATION.md for completed OAL immigration
    
    PLAN.md:
    - Phase 1 marked COMPLETE, Phase 2 split into done/remaining items
    - Challenge 1 (OAL) marked SOLVED with implementation details
    - Challenge 2 (MAL/LAL) rewritten to reflect actual architecture
    - Challenge 3 marked PARTIALLY SOLVED (AnnotationScan + SourceReceiverImpl 
done)
    - Challenge 4 gains Phase 1 implementation summary
    - Upstream tracker: OAL done (no upstream change needed)
    
    OAL-IMMIGRATION.md:
    - All 3 steps marked DONE
    - Step 1 rewritten: no collecting listeners, scans output directory
    - Step 2 rewritten: same-FQCN replacement (3 classes) instead of
      extending + hooking via ModuleWiringBridge
    - Step 3 updated: MeterSystem deferred to MAL, reflect-config to Phase 3
    - Files section updated to list all 6 actual files created/modified
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 OAL-IMMIGRATION.md | 161 +++++++++++++++++++++++++++--------------------------
 PLAN.md            |  80 +++++++++++++++++---------
 2 files changed, 137 insertions(+), 104 deletions(-)

diff --git a/OAL-IMMIGRATION.md b/OAL-IMMIGRATION.md
index 01d70fd..1917177 100644
--- a/OAL-IMMIGRATION.md
+++ b/OAL-IMMIGRATION.md
@@ -1,39 +1,37 @@
-# Phase 2: OAL Build-Time Pre-Compilation
+# Phase 2: OAL Build-Time Pre-Compilation — COMPLETE
 
 ## Context
 
-OAL engine generates metrics, builder, and dispatcher classes at runtime via 
Javassist (`ClassPool.makeClass()` → `CtClass.toClass()`). GraalVM native image 
doesn't support runtime bytecode generation. Additionally, Guava's 
`ClassPath.from()` — used by `AnnotationScan.scan()` and 
`SourceReceiverImpl.scan()` — doesn't work in native image (no JAR-based 
classpath). So we cannot rely on classpath scanning to discover pre-generated 
classes.
+OAL engine generates metrics, builder, and dispatcher classes at runtime via 
Javassist (`ClassPool.makeClass()` → `CtClass.toClass()`). GraalVM native image 
doesn't support runtime bytecode generation. Additionally, Guava's 
`ClassPath.from()` — used by `AnnotationScan.scan()` and 
`SourceReceiverImpl.scan()` — doesn't work in native image (no JAR-based 
classpath).
 
-**Solution**: Run OAL engine at build time, export `.class` files + a manifest 
of class names. At runtime, load classes by name from the manifest and register 
them **directly** with `StreamAnnotationListener` and `DispatcherManager` — no 
classpath scanning.
+**Solution**: Run OAL engine at build time, export `.class` files + manifests. 
Replace upstream classes with same-FQCN versions that load from manifests 
instead of scanning or generating code.
 
 ---
 
-## Step 1: Build-Time OAL Class Export Tool
+## Step 1: Build-Time OAL Class Export Tool — DONE
 
-**Module**: `build-tools/oal-exporter` (skeleton exists)
+**Module**: `build-tools/oal-exporter`
 
-**Create**: `OALClassExporter.java` — main class that:
+**Created**: `OALClassExporter.java` — main class that:
 
-1. For each of the 9 `OALDefine` configs: instantiate `OALEngineV2`, set 
`StorageBuilderFactory.Default`, call `engine.start()` (Javassist generates 
classes in the build JVM)
-2. Export bytecode via existing debug mechanism: 
`generator.setOpenEngineDebug(true)` + 
`OALClassGeneratorV2.setGeneratedFilePath(outputDir)` — `writeGeneratedFile()` 
writes `.class` files using `ctClass.toBytecode(DataOutputStream)`
-3. After each engine run, call `engine.notifyAllListeners()` with **collecting 
listeners** that record class names (not real registration)
-4. Write manifest files:
-   - `META-INF/oal-metrics-classes.txt` — one fully-qualified class name per 
line
-   - `META-INF/oal-dispatcher-classes.txt` — one fully-qualified class name 
per line
-   - `META-INF/oal-disabled-sources.txt` — one source name per line
-5. Package output directory into a JAR artifact
+1. Validates all 9 OAL script files are on the classpath
+2. Initializes `DefaultScopeDefine` by scanning `@ScopeDeclaration` 
annotations (OAL enricher needs scope metadata)
+3. For each of the 9 `OALDefine` configs: instantiates `OALEngineV2`, enables 
debug output (`setOpenEngineDebug(true)` + `setGeneratedFilePath()`), calls 
`engine.start()` which parses OAL → enriches → generates `.class` files via 
Javassist
+4. Scans the output directory for generated `.class` files and writes OAL 
manifests:
+   - `META-INF/oal-metrics-classes.txt` — ~620 fully-qualified class names
+   - `META-INF/oal-dispatcher-classes.txt` — ~45 fully-qualified class names
+   - `META-INF/oal-disabled-sources.txt` — disabled source names from 
`disable.oal`
+5. Runs Guava `ClassPath.from()` scan at build time to produce 6 
annotation/interface manifests under `META-INF/annotation-scan/`:
+   - `ScopeDeclaration.txt` — classes annotated with `@ScopeDeclaration`
+   - `Stream.txt` — classes annotated with `@Stream` (hardcoded only, not 
OAL-generated)
+   - `Disable.txt` — classes annotated with `@Disable`
+   - `MultipleDisable.txt` — classes annotated with `@MultipleDisable`
+   - `SourceDispatcher.txt` — concrete implementations of `SourceDispatcher` 
interface (hardcoded only)
+   - `ISourceDecorator.txt` — concrete implementations of `ISourceDecorator` 
interface
 
-**Note on listeners**: `engine.start()` already generates and loads classes, 
storing them in internal `metricsClasses`/`dispatcherClasses` lists. 
`notifyAllListeners()` iterates these lists. We provide lightweight listeners 
that just collect class names for the manifest, not real stream processors.
+**Key difference from original plan**: No "collecting listeners" needed. 
`engine.start()` generates `.class` files directly to disk via the debug API. 
We scan the output directory for class files rather than hooking into engine 
callbacks.
 
-**Dependencies** for `oal-exporter/pom.xml`:
-- `oal-rt` — engine, generator, parser, enricher, FreeMarker templates
-- `server-core` — `OALDefine` subclasses, source classes, annotations, 
`StorageBuilderFactory`
-- `server-starter` — OAL script resource files (`oal/*.oal`)
-- Receiver plugins that define `OALDefine` subclasses (for `BrowserOALDefine`, 
`MeshOALDefine`, etc.)
-
-**Build integration**: `exec-maven-plugin` runs `OALClassExporter.main()` 
during `process-classes` phase. Then `maven-jar-plugin` with a custom 
classifier packages the generated `.class` files + manifest into 
`oal-generated-classes.jar`.
-
-### 9 OAL Defines to process
+### 9 OAL Defines processed
 
 | Define | Config File | Source Package | Catalog |
 |--------|-------------|----------------|---------|
@@ -55,73 +53,82 @@ OAL engine generates metrics, builder, and dispatcher 
classes at runtime via Jav
 
 ---
 
-## Step 2: Runtime Direct Registration (No Classpath Scanning)
+## Step 2: Runtime Registration via Same-FQCN Replacement Classes — DONE
 
-**Create**: `PrecompiledOALEngineLoaderService.java` in `oap-graalvm-server`
+Instead of extending upstream classes or hooking via `ModuleWiringBridge`, we 
use **same-FQCN replacement**: create classes in `oap-graalvm-server` with the 
exact same fully-qualified class name as the upstream class. Maven classpath 
precedence ensures our version is loaded instead of the upstream version.
 
-Extends `OALEngineLoaderService`. Overrides `load()` to:
-1. On first invocation: read manifest files from classpath resources
-2. For each metrics class name: `Class.forName(name)` → 
`streamAnnotationListener.notify(clazz)` (same as `StreamAnnotationListener` — 
routes to `MetricsStreamProcessor.create()`)
-3. For each dispatcher class name: `Class.forName(name)` → 
`dispatcherDetectorListener.addIfAsSourceDispatcher(clazz)` (same as 
`DispatcherManager.addIfAsSourceDispatcher()`)
-4. For each disabled source: `DisableRegister.INSTANCE.add(name)`
-5. All subsequent `load()` calls → no-op (classes already registered)
+### 3 replacement classes created:
 
-```
-// Pseudocode
-public void load(OALDefine define) throws ModuleStartException {
-    if (registered) return;
-    registered = true;
-
-    StreamAnnotationListener streamListener = new 
StreamAnnotationListener(moduleManager);
-    DispatcherDetectorListener dispatcherListener = 
moduleManager.find(CoreModule.NAME)
-        
.provider().getService(SourceReceiver.class).getDispatcherDetectorListener();
-
-    for (String className : readManifest("META-INF/oal-metrics-classes.txt")) {
-        streamListener.notify(Class.forName(className));
-    }
-    for (String className : 
readManifest("META-INF/oal-dispatcher-classes.txt")) {
-        dispatcherListener.addIfAsSourceDispatcher(Class.forName(className));
-    }
-    for (String source : readManifest("META-INF/oal-disabled-sources.txt")) {
-        DisableRegister.INSTANCE.add(source);
-    }
-}
-```
+**1. `OALEngineLoaderService`** 
(`oap-graalvm-server/.../core/oal/rt/OALEngineLoaderService.java`)
+
+Same FQCN as upstream 
`org.apache.skywalking.oap.server.core.oal.rt.OALEngineLoaderService`. On first 
`load()` call:
+- Reads `META-INF/oal-disabled-sources.txt` → registers with `DisableRegister`
+- Reads `META-INF/oal-metrics-classes.txt` → `Class.forName()` → 
`StreamAnnotationListener.notify()`
+- Reads `META-INF/oal-dispatcher-classes.txt` → `Class.forName()` → 
`DispatcherDetectorListener.addIfAsSourceDispatcher()`
+- All subsequent `load()` calls are no-ops (all classes registered on first 
call regardless of which `OALDefine` triggered it)
+
+**2. `AnnotationScan`** 
(`oap-graalvm-server/.../core/annotation/AnnotationScan.java`)
+
+Same FQCN as upstream 
`org.apache.skywalking.oap.server.core.annotation.AnnotationScan`. Instead of 
Guava `ClassPath.from()` scanning, reads manifest files from 
`META-INF/annotation-scan/{AnnotationSimpleName}.txt`. Each registered 
`AnnotationListener` is matched against its corresponding manifest.
+
+**3. `SourceReceiverImpl`** 
(`oap-graalvm-server/.../core/source/SourceReceiverImpl.java`)
 
-**Wiring**: Replace the default `OALEngineLoaderService` after 
`CoreModuleProvider.prepare()`. `registerServiceImplementation()` uses 
`HashMap.put()` — re-registration overwrites. Add a post-prepare hook in 
`ModuleWiringBridge` for `CoreModuleProvider` to swap in 
`PrecompiledOALEngineLoaderService`.
+Same FQCN as upstream 
`org.apache.skywalking.oap.server.core.source.SourceReceiverImpl`. `scan()` 
reads from `META-INF/annotation-scan/SourceDispatcher.txt` and 
`META-INF/annotation-scan/ISourceDecorator.txt` instead of Guava classpath 
scanning.
 
-**Timing**: `CoreModuleProvider.start()` calls 
`oalEngineLoaderService.load(DisableOALDefine.INSTANCE)` first, then 
`annotationScan.scan()` and `receiver.scan()`. Our 
`PrecompiledOALEngineLoaderService.load()` runs first and registers ALL 
pre-generated classes (not just disable.oal). The subsequent 
`annotationScan.scan()` and `receiver.scan()` will also run, but for Phase 2 
(JVM mode), they work fine via `ClassPath.from()` and simply re-discover static 
classes. The OAL classes are alread [...]
+### Key differences from original plan:
+- **No extending** — same-FQCN replacement instead of subclassing
+- **No `ModuleWiringBridge` changes** — classpath precedence handles the swap 
automatically
+- **3 replacement classes, not 1** — `AnnotationScan` and `SourceReceiverImpl` 
also needed replacement
+- **Classpath scanning fully eliminated** — not deferred to Phase 3; 
annotation manifests solve it now
 
 ---
 
-## Step 3: Why Class.forName() Works for Native Image
+## Step 3: Class Loading and Remaining Scans — DONE
 
-`Class.forName()` is supported in GraalVM native image when classes are 
registered in `reflect-config.json`. Since all pre-generated classes are on the 
classpath at native-image build time, the GraalVM compiler includes them in the 
binary. The `reflect-config.json` entries (Phase 3 task) enable runtime 
`Class.forName()` lookup. For Phase 2 (JVM mode), `Class.forName()` works 
naturally.
+### `Class.forName()` in native image
+`Class.forName()` is supported in GraalVM native image when classes are 
registered in `reflect-config.json`. Since all pre-generated classes are on the 
classpath at native-image build time, the GraalVM compiler includes them in the 
binary. The `reflect-config.json` entries (Phase 3 task) will enable runtime 
`Class.forName()` lookup. For Phase 2 (JVM mode), `Class.forName()` works 
naturally.
 
-The 3 OAL-internal scans (`MetricsHolder`, `DefaultMetricsFunctionRegistry`, 
`FilterMatchers`) only run during OAL engine execution — they happen at **build 
time** in our tool, not at runtime. So they're automatically solved.
+### OAL-internal scans — build-time only
+The 3 OAL-internal scans (`MetricsHolder`, `DefaultMetricsFunctionRegistry`, 
`FilterMatchers`) only run inside the OAL engine during `engine.start()`. They 
happen at **build time** in `OALClassExporter`, not at runtime. Automatically 
solved.
+
+### `MeterSystem` — deferred to MAL immigration
+`MeterSystem` uses Guava `ClassPath.from()` to discover meter function classes 
at runtime. This is part of the MAL/meter subsystem, not OAL. Will be addressed 
as part of MAL immigration (Phase 2 remaining work).
+
+### `reflect-config.json` — deferred to Phase 3
+GraalVM reflection configuration for `Class.forName()` calls on OAL-generated 
and manifest-listed classes will be generated in Phase 3.
 
 ---
 
-## Files to Create
+## Files Created
+
+1. **`build-tools/oal-exporter/src/main/java/.../OALClassExporter.java`**
+   - Build-time tool: runs 9 OAL defines, exports `.class` files, writes OAL 
manifests + annotation/interface manifests
+
+2. 
**`oap-graalvm-server/src/main/java/.../core/oal/rt/OALEngineLoaderService.java`**
+   - Same-FQCN replacement: loads pre-compiled OAL classes from manifests
 
-1. 
**`build-tools/oal-exporter/src/main/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporter.java`**
-   - Main class: iterates 9 OAL defines, runs engine, exports bytecode + 
writes manifest files
+3. 
**`oap-graalvm-server/src/main/java/.../core/annotation/AnnotationScan.java`**
+   - Same-FQCN replacement: reads annotation manifests instead of Guava 
classpath scanning
 
-2. 
**`oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/graalvm/PrecompiledOALEngineLoaderService.java`**
-   - Extends `OALEngineLoaderService`, loads from manifest + direct 
registration
+4. 
**`oap-graalvm-server/src/main/java/.../core/source/SourceReceiverImpl.java`**
+   - Same-FQCN replacement: reads dispatcher/decorator manifests instead of 
Guava classpath scanning
 
-## Files to Modify
+5. **`build-tools/oal-exporter/src/test/java/.../OALClassExporterTest.java`**
+   - 3 tests: OAL script coverage, classpath availability, full export with 
manifest verification
 
-1. **`build-tools/oal-exporter/pom.xml`** — add dependencies, configure 
`exec-maven-plugin` + `maven-jar-plugin`
-2. **`oap-graalvm-server/pom.xml`** — add dependency on `oal-exporter` 
generated JAR
-3. **`oap-graalvm-server/.../ModuleWiringBridge.java`** — post-prepare hook to 
replace `OALEngineLoaderService` with `PrecompiledOALEngineLoaderService`
-4. **`Makefile`** — ensure build order: skywalking → oal-exporter → 
oap-graalvm-server
+6. **`oap-graalvm-server/src/test/java/.../PrecompiledRegistrationTest.java`**
+   - 12 tests: manifest vs Guava scan comparison, OAL class loading, scope 
registration, source→dispatcher→metrics chain consistency
+
+## Files Modified
+
+1. **`build-tools/oal-exporter/pom.xml`** — dependencies + `exec-maven-plugin` 
+ `maven-jar-plugin`
+2. **`oap-graalvm-server/pom.xml`** — dependency on `oal-exporter` generated 
JAR
 
 ## Key Upstream Files (read-only)
 
 - `OALEngineV2.java` — `start()` (parse → enrich → generate), 
`notifyAllListeners()` (register)
 - `OALClassGeneratorV2.java` — `setOpenEngineDebug(true)`, 
`setGeneratedFilePath()`, `writeGeneratedFile()` exports via 
`ctClass.toBytecode()`
-- `OALEngineLoaderService.java` — `load()` creates engine, sets listeners, 
calls `start()`+`notifyAllListeners()`
+- `OALEngineLoaderService.java` (upstream) — `load()` creates engine, sets 
listeners, calls `start()`+`notifyAllListeners()`
 - `StorageBuilderFactory.java:67-78` — `Default` impl uses `metrics-builder` 
template path
 - `StreamAnnotationListener.java` — `notify(Class)` reads `@Stream`, routes to 
`MetricsStreamProcessor.create()`
 - `CoreModuleProvider.java:356-357` — registers `OALEngineLoaderService` in 
`prepare()`
@@ -136,18 +143,16 @@ The 3 OAL-internal scans (`MetricsHolder`, 
`DefaultMetricsFunctionRegistry`, `Fi
 make build-distro
 
 # 2. Check generated classes exist
-ls build-tools/oal-exporter/target/generated-oal-classes/
-# Should have: org/apache/skywalking/.../oal/rt/metrics/*.class
-#              org/apache/skywalking/.../oal/rt/metrics/builder/*.class
-#              org/apache/skywalking/.../oal/rt/dispatcher/*.class
+ls 
build-tools/oal-exporter/target/generated-oal-classes/org/apache/skywalking/oap/server/core/source/oal/rt/metrics/
+ls 
build-tools/oal-exporter/target/generated-oal-classes/org/apache/skywalking/oap/server/core/source/oal/rt/dispatcher/
 
 # 3. Check manifest files
 cat 
build-tools/oal-exporter/target/generated-oal-classes/META-INF/oal-metrics-classes.txt
 cat 
build-tools/oal-exporter/target/generated-oal-classes/META-INF/oal-dispatcher-classes.txt
-# Should list all generated class names
 
-# 4. Verify JAR packaging
-jar tf build-tools/oal-exporter/target/oal-generated-classes.jar
+# 4. Check annotation scan manifests
+ls 
build-tools/oal-exporter/target/generated-oal-classes/META-INF/annotation-scan/
 
-# 5. Verify oap-graalvm-server compiles with the generated JAR dependency
-```
\ No newline at end of file
+# 5. Verify tests pass
+make build-distro   # runs both OALClassExporterTest and 
PrecompiledRegistrationTest
+```
diff --git a/PLAN.md b/PLAN.md
index 74d0845..a89051a 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -37,40 +37,54 @@ Build and package Apache SkyWalking OAP server as a GraalVM 
native image on JDK
 
 ---
 
-## Challenge 1: OAL Runtime Class Generation (Javassist)
+## Challenge 1: OAL Runtime Class Generation (Javassist) — SOLVED
 
 ### What Happens
 OAL V2 generates metrics/builder/dispatcher classes at startup via Javassist 
(`ClassPool.makeClass()` → `CtClass.toClass()`). Already has 
`writeGeneratedFile()` for debug export.
 
 ### Approach (this repo)
-All `.oal` scripts are known. Run OAL engine at build time, export `.class` 
files, load them directly in native-image mode.
+All `.oal` scripts are known. Run OAL engine at build time, export `.class` 
files, load them directly at runtime from manifests. See 
[OAL-IMMIGRATION.md](OAL-IMMIGRATION.md) for details.
+
+### What Was Built
+- `OALClassExporter` processes all 9 OAL defines, exports ~620 metrics 
classes, ~620 builder classes, ~45 dispatchers
+- 3 manifest files: `oal-metrics-classes.txt`, `oal-dispatcher-classes.txt`, 
`oal-disabled-sources.txt`
+- Same-FQCN replacement `OALEngineLoaderService` loads pre-compiled classes 
from manifests instead of running Javassist
 
 ### Upstream Changes Needed
-- Potentially expose build-time class export as a stable API (currently 
debug-only)
+- None. Build-time class export works via existing debug API 
(`setOpenEngineDebug(true)` + `setGeneratedFilePath()`)
 
 ---
 
-## Challenge 2: MAL and LAL (Groovy)
+## Challenge 2: MAL and LAL (Groovy + Javassist)
 
 ### What Happens
-MAL uses `GroovyShell` + `Binding` for meter rules. LAL uses `GroovyShell` + 
`DelegatingScript` + `Closure` + AST customizers.
+- MAL uses `GroovyShell` + `DelegatingScript` for meter rule expressions 
(~1188 rules). Also, `MeterSystem.create()` uses Javassist to dynamically 
generate one meter subclass per metric rule.
+- LAL uses `GroovyShell` + `@CompileStatic` + `LALPrecompiledExtension` for 
log analysis scripts (10 rules).
 
 ### Approach (this repo)
-Groovy static compilation (`@CompileStatic`). Pre-compile all MAL/LAL rule 
files at build time, export `.class` files.
+Run full MAL/LAL initialization at build time via `build-tools/mal-compiler`. 
Export Javassist-generated `.class` files + compiled Groovy script bytecode. At 
runtime, load from manifests. See [MAL-IMMIGRATION.md](MAL-IMMIGRATION.md) for 
details.
+
+### Key finding: MAL cannot use @CompileStatic
+MAL expressions rely on `propertyMissing()` for sample name resolution and 
`ExpandoMetaClass` on `Number` for arithmetic operators — fundamentally dynamic 
Groovy features. Pre-compilation uses standard dynamic Groovy (same 
`CompilerConfiguration` as upstream). LAL already uses `@CompileStatic`.
 
 ### Risks
-- `@CompileStatic` may not cover all dynamic features (track and work around)
-- May need DSL adjustments (upstream PRs)
+- Dynamic Groovy MOP may not work in GraalVM native image (Phase 3 concern)
+- If `ExpandoMetaClass` fails in native image: fallback to upstream DSL changes
 
 ---
 
-## Challenge 3: Classpath Scanning (Guava ClassPath)
+## Challenge 3: Classpath Scanning (Guava ClassPath) — PARTIALLY SOLVED
 
 ### What Happens
 `ClassPath.from()` used in `SourceReceiverImpl.scan()`, `AnnotationScan`, 
`MeterSystem`, `DefaultMetricsFunctionRegistry`, `FilterMatchers`, 
`MetricsHolder`.
 
-### Approach (this repo)
-Run classpath scanning during build-time pre-compilation. Verify all 
metrics/log-processing classes are discovered. Export scan results as static 
class index. Native-image mode loads from index.
+### What Was Solved
+`AnnotationScan` and `SourceReceiverImpl` replaced with same-FQCN classes that 
read from build-time manifests. 6 annotation/interface manifests under 
`META-INF/annotation-scan/`: `ScopeDeclaration`, `Stream`, `Disable`, 
`MultipleDisable`, `SourceDispatcher`, `ISourceDecorator`.
+
+`DefaultMetricsFunctionRegistry`, `FilterMatchers`, `MetricsHolder` — these 
only run inside the OAL engine at build time, not at runtime. Automatically 
solved.
+
+### What Remains
+`MeterSystem` uses Guava `ClassPath.from()` to scan for meter function classes 
at runtime. This needs a manifest or build-time scan as part of MAL immigration.
 
 ---
 
@@ -84,6 +98,12 @@ Run classpath scanning during build-time pre-compilation. 
Verify all metrics/log
 2. **Simplified config file**: Only knobs for selected providers
 3. **Config loading**: **No reflection.** At build time, read 
`application.yml` + scan `ModuleConfig` subclass fields → generate Java code 
that directly sets config fields (e.g. `config.restPort = 12800;`). Eliminates 
`Field.setAccessible`/`field.set` and the need for `reflect-config.json` for 
config classes.
 
+### What Was Built (Phase 1)
+- `FixedModuleManager` — direct module/provider construction, no SPI
+- `ModuleWiringBridge` — wires all selected modules/providers
+- `GraalVMOAPServerStartUp` — entry point
+- `application.yml` — simplified config for selected providers
+
 ---
 
 ## Challenge 5: Additional GraalVM Risks
@@ -102,19 +122,26 @@ Run classpath scanning during build-time pre-compilation. 
Verify all metrics/log
 
 ## Proposed Phases
 
-### Phase 1: Build System Setup
-- [ ] Set up Maven + Makefile in this repo
-- [ ] Build skywalking submodule as a dependency
+### Phase 1: Build System Setup — COMPLETE
+- [x] Set up Maven + Makefile in this repo
+- [x] Build skywalking submodule as a dependency
 - [ ] Set up GraalVM JDK 25 in CI
-- [ ] Create a JVM-mode starter with fixed module wiring (validate approach 
before native-image)
-- [ ] Simplified config file for selected modules
-
-### Phase 2: Build-Time Pre-Compilation & Verification
-- [ ] Makefile/Maven step: run OAL engine → export `.class` files
-- [ ] Makefile/Maven step: static-compile MAL Groovy scripts → export `.class` 
files
-- [ ] Makefile/Maven step: static-compile LAL Groovy scripts → export `.class` 
files
-- [ ] Makefile/Maven step: run classpath scanning → export class index
-- [ ] Verify all metrics/log-processing classes are correctly discovered
+- [x] Create a JVM-mode starter with fixed module wiring (`FixedModuleManager` 
+ `ModuleWiringBridge` + `GraalVMOAPServerStartUp`)
+- [x] Simplified config file for selected modules (`application.yml`)
+
+### Phase 2: Build-Time Pre-Compilation & Verification — IN PROGRESS
+
+**OAL immigration — COMPLETE:**
+- [x] OAL engine → export `.class` files (`OALClassExporter`, 9 defines, ~620 
metrics, ~45 dispatchers)
+- [x] Classpath scanning → export class index (6 annotation/interface 
manifests in `oal-exporter`)
+- [x] Runtime registration from manifests (3 same-FQCN replacement classes: 
`OALEngineLoaderService`, `AnnotationScan`, `SourceReceiverImpl`)
+- [x] Verification tests (`OALClassExporterTest` — 3 tests, 
`PrecompiledRegistrationTest` — 12 tests)
+
+**Remaining:**
+- [ ] MAL Groovy pre-compilation (`build-tools/mal-compiler` skeleton exists)
+- [ ] LAL Groovy pre-compilation (can be part of mal-compiler)
+- [ ] `MeterSystem` classpath scan: uses Guava scanning for meter function 
classes — needs manifest or build-time scan (part of MAL immigration)
+- [ ] Config generator (`build-tools/config-generator` skeleton exists) — 
eliminate `Field.setAccessible` reflection in config loading
 - [ ] Package everything into native-image classpath
 
 ### Phase 3: Native Image Build
@@ -122,6 +149,7 @@ Run classpath scanning during build-time pre-compilation. 
Verify all metrics/log
 - [ ] Run tracing agent to capture reflection/resource/JNI metadata
 - [ ] Configure gRPC/Netty/Protobuf for native image
 - [ ] GraalVM Feature class for SkyWalking-specific registrations
+- [ ] `reflect-config.json` for OAL-generated classes (`Class.forName()` calls)
 - [ ] Get OAP server booting as native image with BanyanDB
 
 ### Phase 4: Harden & Test
@@ -135,6 +163,6 @@ Run classpath scanning during build-time pre-compilation. 
Verify all metrics/log
 ---
 
 ## Upstream Changes Tracker
-- [ ] OAL engine: stable build-time class export API
-- [ ] MAL/LAL: DSL adjustments for Groovy static compilation (if needed)
-- [ ] Other findings during implementation
\ No newline at end of file
+- [x] OAL engine: build-time class export works via existing debug API (no 
upstream change needed)
+- [ ] MAL/LAL: Groovy static compilation / DSL adjustments (not started)
+- [ ] Other findings during implementation

Reply via email to