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


The following commit(s) were added to refs/heads/main by this push:
     new a5f4ad8  Move docs to docs/ directory, add website menu, configuration 
reference, and README
a5f4ad8 is described below

commit a5f4ad8e20dc6ae1b51c9e6a88179ccbf1fbe178
Author: Wu Sheng <[email protected]>
AuthorDate: Fri Feb 27 08:41:17 2026 +0800

    Move docs to docs/ directory, add website menu, configuration reference, 
and README
    
    - Move 5 root-level doc files (DISTRO-POLICY, 
OAL/MAL/LAL/CONFIG-INIT-IMMIGRATION)
      into docs/ with lowercase names as the canonical location
    - Add docs/menu.yml for website generation
    - Add docs/README.md as the website landing page
    - Add docs/configuration.md with full module/setting/env-var reference
    - Slim down root README.md to a brief overview linking to docs/
    - Update CLAUDE.md references to point to docs/ paths
---
 CLAUDE.md                  |  10 +-
 CONFIG-INIT-IMMIGRATION.md | 253 --------------
 DISTRO-POLICY.md           | 322 ------------------
 LAL-IMMIGRATION.md         | 352 -------------------
 MAL-IMMIGRATION.md         | 817 ---------------------------------------------
 OAL-IMMIGRATION.md         | 162 ---------
 README.md                  |  78 +----
 docs/README.md             | 111 ++++++
 docs/configuration.md      | 628 ++++++++++++++++++++++++++++++++++
 docs/menu.yml              |  34 ++
 10 files changed, 788 insertions(+), 1979 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index ce04263..397b0ea 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -8,13 +8,13 @@
 - `oap-graalvm-server/` — GraalVM-ready OAP server module (JVM distro) with 
same-FQCN replacement classes and comprehensive test suites.
 - `oap-graalvm-native/` — Native image module: `native-maven-plugin` 
configuration, native-specific `log4j2.xml`, `log4j2-reflect-config.json`, and 
native distribution assembly.
 - `docker/` — `Dockerfile.native` (runtime image) and `docker-compose.yml` 
(BanyanDB + OAP native).
-- `DISTRO-POLICY.md` — Build plan with phase tracking and module selection.
+- `docs/` — Documentation: distro-policy, configuration, OAL/MAL/LAL 
immigration, config initialization.
 - Root-level Maven + Makefile — Orchestrates building on top of the submodule.
 
 ## Key Principles
 1. **Minimize upstream changes.** SkyWalking is a submodule. Changes to it 
require separate upstream PRs and syncing back.
 2. **Build-time class export.** All runtime code generation (OAL via 
Javassist, MAL/LAL via Groovy) runs at build time. Export `.class` files into 
native-image classpath.
-3. **Fixed module wiring.** Module/provider selection is hardcoded in this 
distro — no SPI discovery. See DISTRO-POLICY.md for the full module table.
+3. **Fixed module wiring.** Module/provider selection is hardcoded in this 
distro — no SPI discovery. See docs/distro-policy.md for the full module table.
 4. **JDK 25.** Already compiles and runs.
 
 ## Technical Notes
@@ -24,13 +24,13 @@
 - **Combination pattern**: Multiple YAML files from different data sources 
(otel, telegraf, zabbix) may define metrics with the same name. The precompiler 
assigns deterministic suffixes (`_1`, `_2`, etc.) and tracks expression hashes 
for unambiguous resolution.
 - **Same-FQCN replacement**: Classes in 
`oap-libs-for-graalvm/*/src/main/java/` with the same fully-qualified class 
name as upstream classes are repackaged via `maven-shade-plugin` (original 
`.class` excluded). Used for `DSL.java`, `SampleFamily.java`, 
`MeterSystem.java`, etc.
 - **Classpath scanning**: Guava `ClassPath.from()` used in multiple places. 
Run at build-time pre-compilation as verification gate, export static class 
index.
-- **Config loading**: `YamlConfigLoaderUtils.copyProperties()` replaced with 
same-FQCN version that uses Lombok setters instead of `Field.setAccessible()`. 
See [CONFIG-INIT-IMMIGRATION.md](CONFIG-INIT-IMMIGRATION.md).
+- **Config loading**: `YamlConfigLoaderUtils.copyProperties()` replaced with 
same-FQCN version that uses Lombok setters instead of `Field.setAccessible()`. 
See [docs/config-init-immigration.md](docs/config-init-immigration.md).
 - **Reflection metadata**: Precompiler auto-generates `reflect-config.json` by 
scanning Armeria HTTP handlers, GraphQL resolvers/types, config POJOs, and 
OAL/MAL/LAL manifests. `log4j2-reflect-config.json` is manually maintained for 
Log4j2 plugin classes.
 - **Native image**: `oap-graalvm-native` uses `native-maven-plugin` with 
`-Pnative` profile. Console-only `log4j2.xml` avoids RollingFile reflection 
chain. ~203MB binary, boots to full module init.
 
 ## Test Suites
 - **MAL**: 71 YAML files covered by 73 test classes (1,281 assertions). See 
`oap-graalvm-server/src/test/CLAUDE.md` for test generation instructions and 
`oap-graalvm-server/src/test/MAL-COVERAGE.md` for coverage tracking.
-- **LAL**: 8 YAML files covered by 5 test classes (19 assertions). See 
[LAL-IMMIGRATION.md](LAL-IMMIGRATION.md) for details.
+- **LAL**: 8 YAML files covered by 5 test classes (19 assertions). See 
[docs/lal-immigration.md](docs/lal-immigration.md) for details.
 
 Both suites use dual-path comparison: Path A (fresh GroovyShell compilation) 
vs Path B (pre-compiled class from build-time JAR). Both paths must produce 
identical results.
 
@@ -65,4 +65,4 @@ docker compose -f docker/docker-compose.yml up
 - **Storage**: BanyanDB
 - **Cluster**: Standalone, Kubernetes
 - **Configuration**: Kubernetes
-- **Receivers/Query/Analyzers/Alarm/Telemetry/Other**: Full feature set (see 
DISTRO-POLICY.md for details)
\ No newline at end of file
+- **Receivers/Query/Analyzers/Alarm/Telemetry/Other**: Full feature set (see 
docs/distro-policy.md for details)
\ No newline at end of file
diff --git a/CONFIG-INIT-IMMIGRATION.md b/CONFIG-INIT-IMMIGRATION.md
deleted file mode 100644
index 3b96b61..0000000
--- a/CONFIG-INIT-IMMIGRATION.md
+++ /dev/null
@@ -1,253 +0,0 @@
-# Config Initialization — Eliminate Reflection in Config Loading
-
-## Context
-
-`YamlConfigLoaderUtils.copyProperties()` uses `Field.setAccessible(true)` +
-`field.set()` to populate `ModuleConfig` objects from YAML properties. This
-reflection pattern is problematic for GraalVM native image — every config field
-would require `reflect-config.json` entries, and `setAccessible()` is 
restricted.
-
-Since our module/provider set is fixed (37 modules, see DISTRO-POLICY.md), we
-can generate hardcoded field-setting code at build time that eliminates all
-config-related reflection.
-
----
-
-## Problem
-
-In `ModuleDefine.prepare()` and in `BanyanDBConfigLoader`,
-`copyProperties()` iterates property names, looks up fields by name via
-reflection, and sets them:
-
-```java
-Field field = getDeclaredField(destClass, propertyName);
-field.setAccessible(true);     // restricted in native image
-field.set(dest, value);        // needs reflect-config.json
-```
-
-This requires:
-- `getDeclaredField()` with class hierarchy walk
-- `field.setAccessible(true)` to bypass private access
-- `field.set()` for every property
-
----
-
-## Solution: Same-FQCN Replacement of YamlConfigLoaderUtils
-
-Generate a replacement `YamlConfigLoaderUtils.java` with the same FQCN
-(`org.apache.skywalking.oap.server.library.util.YamlConfigLoaderUtils`) that
-dispatches by config object type and sets fields directly — no
-`Field.setAccessible()`, no `getDeclaredField()` scan.
-
-This is one of 23 same-FQCN replacement classes in the distro (see 
DISTRO-POLICY.md for full list).
-
-### Field Access Strategy (per field)
-
-| Strategy | Condition | Example |
-|---|---|---|
-| **Lombok setter** | All non-final fields (class-level `@Setter` added via 
`-for-graalvm` modules) | `cfg.setRole((String) value)` |
-| **Getter + clear + addAll** | Final collection field | 
`cfg.getDownsampling().clear(); cfg.getDownsampling().addAll((List) value)` |
-| **Error** | Unknown config type | `throw new 
IllegalArgumentException("Unknown config type: ...")` |
-
-All config classes that previously lacked `@Setter` now have it added via
-same-FQCN replacement classes in the `-for-graalvm` modules. No VarHandle, no
-reflection fallback. The generator fails at build time if any non-final field
-lacks a setter.
-
-### Generated Code Structure
-
-```java
-package org.apache.skywalking.oap.server.library.util;
-
-public class YamlConfigLoaderUtils {
-    // No VarHandle, no reflection. Pure setter-based.
-
-    // Type-dispatch: check instanceof, delegate to type-specific method
-    public static void copyProperties(Object dest, Properties src,
-            String moduleName, String providerName)
-            throws IllegalAccessException {
-        if (dest instanceof CoreModuleConfig) {
-            copyToCoreModuleConfig((CoreModuleConfig) dest, src, moduleName, 
providerName);
-        } else if (dest instanceof ClusterModuleKubernetesConfig) {
-            
copyToClusterModuleKubernetesConfig((ClusterModuleKubernetesConfig) dest, src, 
moduleName, providerName);
-        }
-        // ... all config types ...
-        else {
-            throw new IllegalArgumentException("Unknown config type: " + 
dest.getClass().getName());
-        }
-    }
-
-    // Per-type: switch on property name, set via Lombok setter
-    private static void copyToCoreModuleConfig(
-            CoreModuleConfig cfg, Properties src,
-            String moduleName, String providerName) {
-        // iterate properties
-        switch (key) {
-            case "role":
-                cfg.setRole((String) value);
-                break;
-            case "persistentPeriod":
-                cfg.setPersistentPeriod((int) value);
-                break;
-            // ... all fields via setters ...
-            default:
-                log.warn("{} setting is not supported in {} provider of {} 
module",
-                    key, providerName, moduleName);
-                break;
-        }
-    }
-    // ... one method per config type ...
-}
-```
-
----
-
-## Build Tool
-
-**Module**: `build-tools/config-generator`
-
-**Main class**: `ConfigInitializerGenerator.java`
-
-### Input
-- Classpath with all SkyWalking module JARs (same dependencies as 
`oap-graalvm-server`)
-- Provider class list — derived from the fixed module table in 
`GraalVMOAPServerStartUp`
-- Extra config class list — BanyanDB nested configs used by 
`BanyanDBConfigLoader`
-
-### Process
-1. For each provider, call `newConfigCreator().type()` to discover the config 
class
-2. For each config class, use Java reflection to scan all declared fields
-   (walking up to `ModuleConfig` superclass)
-3. For each field, check if a setter method exists (`set` + capitalize(name))
-4. **Fail if any non-final field lacks a setter** (all config classes must 
have `@Setter`)
-5. Generate `YamlConfigLoaderUtils.java` with type-dispatch + switch-based 
field assignment
-
-The generator depends on `-for-graalvm` modules (not upstream JARs) so it sees
-config classes with `@Setter` added. Original upstream JARs are forced to
-`provided` scope to prevent classpath shadowing.
-
-### Output
-- 
`oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/library/util/YamlConfigLoaderUtils.java`
-  (same-FQCN replacement)
-
-### Running the generator
-```bash
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal \
-  mvn -pl build-tools/config-generator exec:java \
-  
-Dexec.args="oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/library/util/YamlConfigLoaderUtils.java"
-```
-
----
-
-## Config Classes Inventory
-
-37 modules registered in `GraalVMOAPServerStartUp`. 1 (`AlarmModule`) has null
-ConfigCreator. The rest need coverage.
-
-### Provider Config Classes
-
-| Provider | Config Class | Fields | Setter Status |
-|---|---|---|---|
-| CoreModuleProvider | `CoreModuleConfig` | ~40 | `@Getter` class-level, 
`@Setter` on ~12 fields, rest need VarHandle |
-| BanyanDBStorageProvider | `BanyanDBStorageConfig` | ~5 | `@Getter @Setter` |
-| ClusterModuleStandaloneProvider | (empty config) | 0 | — |
-| ClusterModuleKubernetesProvider | `ClusterModuleKubernetesConfig` | 3 | 
`@Setter` |
-| ConfigmapConfigurationProvider | `ConfigmapConfigurationSettings` | 3 | 
`@Setter` |
-| PrometheusTelemetryProvider | `PrometheusConfig` | 5 | `@Setter` |
-| AnalyzerModuleProvider | `AnalyzerModuleConfig` | ~10 | `@Getter @Setter` |
-| LogAnalyzerModuleProvider | `LogAnalyzerModuleConfig` | 2 | `@Setter` |
-| SharingServerModuleProvider | `SharingServerConfig` | ~15 | Needs check |
-| EnvoyMetricReceiverProvider | `EnvoyMetricReceiverConfig` | ~15 | Needs 
check |
-| KafkaFetcherProvider | `KafkaFetcherConfig` | ~20 | `@Data` (all setters) |
-| ZipkinReceiverProvider | `ZipkinReceiverConfig` | ~20 | Needs check |
-| GraphQLQueryProvider | `GraphQLQueryConfig` | 4 | `@Setter` |
-| HealthCheckerProvider | `HealthCheckerConfig` | 1 | `@Setter` |
-| + others | (simple configs) | 0-5 | Various |
-
-### BanyanDB Config Loading (BanyanDBConfigLoader)
-
-`BanyanDBConfigLoader` (in `storage-banyandb-plugin`) loads config from 
`bydb.yml`
-and `bydb-topn.yml` independently of the standard YAML config loading path. It
-calls `copyProperties()` on nested config objects, so the generated
-`YamlConfigLoaderUtils` must handle all BanyanDB inner classes.
-
-**`loadBaseConfig()`** — reads `bydb.yml`, calls `copyProperties()` 11 times:
-
-| Call target | Type | Handler | Fields |
-|---|---|---|---|
-| `config.getGlobal()` | `Global` | `copyToGlobal()` | 18 (targets, 
maxBulkSize, flushInterval, ...) |
-| `config.getRecordsNormal()` | `RecordsNormal` | `copyToRecordsNormal()` | 8 
(inherited from GroupResource) |
-| `config.getRecordsLog()` | `RecordsLog` | `copyToRecordsLog()` | 8 |
-| `config.getTrace()` | `Trace` | `copyToTrace()` | 8 |
-| `config.getZipkinTrace()` | `ZipkinTrace` | `copyToZipkinTrace()` | 8 |
-| `config.getRecordsBrowserErrorLog()` | `RecordsBrowserErrorLog` | 
`copyToRecordsBrowserErrorLog()` | 8 |
-| `config.getMetricsMin()` | `MetricsMin` | `copyToMetricsMin()` | 8 |
-| `config.getMetricsHour()` | `MetricsHour` | `copyToMetricsHour()` | 8 |
-| `config.getMetricsDay()` | `MetricsDay` | `copyToMetricsDay()` | 8 |
-| `config.getMetadata()` | `Metadata` | `copyToMetadata()` | 8 |
-| `config.getProperty()` | `Property` | `copyToProperty()` | 8 |
-
-**`copyStages()`** — creates warm/cold `Stage` objects, calls 
`copyProperties()` on each:
-
-| Call target | Type | Handler | Fields |
-|---|---|---|---|
-| warm Stage | `Stage` | `copyToStage()` | 7 (name, nodeSelector, shardNum, 
segmentInterval, ttl, replicas, close) |
-| cold Stage | `Stage` | `copyToStage()` | 7 |
-
-**`loadTopNConfig()`** — reads `bydb-topn.yml`, populates `TopN` objects via 
direct
-setter calls (`topN.setName()`, `topN.setGroupByTagNames()`, etc.). Does NOT 
call
-`copyProperties()` — no handler needed.
-
-**Class hierarchy note**: All group config classes (`RecordsNormal`, `Trace`,
-`MetricsMin`, etc.) extend `GroupResource`. The generator walks the class 
hierarchy
-from each subclass up to `GroupResource` and generates setter calls for all 8
-inherited fields (`shardNum`, `segmentInterval`, `ttl`, `replicas`,
-`enableWarmStage`, `enableColdStage`, `defaultQueryStages`,
-`additionalLifecycleStages`). `Trace` doesn't have its own `@Getter @Setter` 
but
-inherits all setters from `GroupResource`.
-
-**`instanceof` ordering**: Inner classes (`Global`, `RecordsNormal`, `Trace`, 
etc.)
-are static inner classes that do NOT extend `BanyanDBStorageConfig`, so the
-`instanceof BanyanDBStorageConfig` check matches only the top-level config. 
Inner
-class checks come after and work independently.
-
-Two extra inner classes (`RecordsTrace`, `RecordsZipkinTrace`) are included in
-EXTRA_CONFIG_CLASSES for completeness, even though `BanyanDBConfigLoader` 
doesn't
-currently call `copyProperties()` on them.
-
----
-
-## Same-FQCN Replacements (Config Initialization)
-
-| Upstream Class | Upstream Location | Replacement Location | What Changed |
-|---|---|---|---|
-| `YamlConfigLoaderUtils` | 
`server-library/library-util/.../util/YamlConfigLoaderUtils.java` | 
`oap-graalvm-server/` (not in library-util-for-graalvm due to 30+ cross-module 
imports) | Complete rewrite. Uses type-dispatch with Lombok `@Setter` methods 
instead of `Field.setAccessible()` + `field.set()`. |
-| `CoreModuleConfig` | `server-core/.../core/CoreModuleConfig.java` | 
`oap-libs-for-graalvm/server-core-for-graalvm/` | Added `@Setter` at class 
level. Upstream only has `@Getter`. |
-| `AnalyzerModuleConfig` | 
`analyzer/agent-analyzer/.../provider/AnalyzerModuleConfig.java` | 
`oap-libs-for-graalvm/agent-analyzer-for-graalvm/` | Added `@Setter` at class 
level. |
-| `LogAnalyzerModuleConfig` | 
`analyzer/log-analyzer/.../provider/LogAnalyzerModuleConfig.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `@Setter` at class 
level. |
-| `EnvoyMetricReceiverConfig` | 
`server-receiver-plugin/envoy-metrics-receiver-plugin/.../EnvoyMetricReceiverConfig.java`
 | `oap-libs-for-graalvm/envoy-metrics-receiver-for-graalvm/` | Added `@Setter` 
at class level. |
-| `OtelMetricReceiverConfig` | 
`server-receiver-plugin/otel-receiver-plugin/.../OtelMetricReceiverConfig.java` 
| `oap-libs-for-graalvm/otel-receiver-for-graalvm/` | Added `@Setter` at class 
level. |
-| `EBPFReceiverModuleConfig` | 
`server-receiver-plugin/skywalking-ebpf-receiver-plugin/.../EBPFReceiverModuleConfig.java`
 | `oap-libs-for-graalvm/ebpf-receiver-for-graalvm/` | Added `@Setter` at class 
level. |
-| `AWSFirehoseReceiverModuleConfig` | 
`server-receiver-plugin/aws-firehose-receiver/.../AWSFirehoseReceiverModuleConfig.java`
 | `oap-libs-for-graalvm/aws-firehose-receiver-for-graalvm/` | Added `@Setter` 
at class level. |
-| `CiliumFetcherConfig` | 
`server-fetcher-plugin/cilium-fetcher-plugin/.../CiliumFetcherConfig.java` | 
`oap-libs-for-graalvm/cilium-fetcher-for-graalvm/` | Added `@Setter` at class 
level. |
-| `StatusQueryConfig` | 
`server-query-plugin/status-query-plugin/.../StatusQueryConfig.java` | 
`oap-libs-for-graalvm/status-query-for-graalvm/` | Added `@Setter` at class 
level. |
-| `HealthCheckerConfig` | `server-health-checker/.../HealthCheckerConfig.java` 
| `oap-libs-for-graalvm/health-checker-for-graalvm/` | Added `@Setter` at class 
level. |
-
-Config replacements (except `YamlConfigLoaderUtils`) are repackaged into their 
respective `-for-graalvm` modules via `maven-shade-plugin`. 
`YamlConfigLoaderUtils` lives in `oap-graalvm-server` because it imports types 
from 30+ modules; the original `.class` is excluded from 
`library-util-for-graalvm` via shade filter.
-
-## Same-FQCN Packaging
-
-The original `YamlConfigLoaderUtils.class` is excluded from the 
`library-util-for-graalvm` shaded JAR. The replacement in 
`oap-graalvm-server.jar` is the only copy on the classpath.
-
----
-
-## Verification
-
-```bash
-# Run generator
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal \
-  mvn -pl build-tools/config-generator exec:java \
-  
-Dexec.args="oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/library/util/YamlConfigLoaderUtils.java"
-
-# Full build (compile + test + package)
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal make build-distro
-```
diff --git a/DISTRO-POLICY.md b/DISTRO-POLICY.md
deleted file mode 100644
index c192373..0000000
--- a/DISTRO-POLICY.md
+++ /dev/null
@@ -1,322 +0,0 @@
-# SkyWalking GraalVM Distro - Distribution Policy
-
-## Goal
-Build and package Apache SkyWalking OAP server as a GraalVM native image on 
JDK 25.
-
-## Architecture Constraints
-- **Submodule**: `skywalking/` is a git submodule of `apache/skywalking.git`. 
All SkyWalking source changes go through upstream PRs. **Minimize upstream 
changes.**
-- **This repo**: Maven + Makefile to orchestrate building on top of the 
submodule. Pre-compilation, GraalVM config, native-image wiring, and the fixed 
module manager live here.
-- **JDK 25**: Already compiles and runs. Not an issue.
-
-## Module Selection (Fixed at Build Time)
-
-| Category | Module | Provider |
-|----------|--------|----------|
-| **Core** | CoreModule | default |
-| **Storage** | StorageModule | BanyanDB |
-| **Cluster** | ClusterModule | Standalone, Kubernetes |
-| **Configuration** | ConfigurationModule | Kubernetes |
-| **Receivers** | SharingServerModule, TraceModule, JVMModule, 
MeterReceiverModule, LogModule, RegisterModule, ProfileModule, BrowserModule, 
EventModule, OtelMetricReceiverModule, MeshReceiverModule, 
EnvoyMetricReceiverModule, ZipkinReceiverModule, ZabbixReceiverModule, 
TelegrafReceiverModule, AWSFirehoseReceiverModule, CiliumFetcherModule, 
EBPFReceiverModule, AsyncProfilerModule, PprofModule, CLRModule, 
ConfigurationDiscoveryModule, KafkaFetcherModule | default providers |
-| **Analyzers** | AnalyzerModule, LogAnalyzerModule, EventAnalyzerModule | 
default providers |
-| **Query** | QueryModule (GraphQL), PromQLModule, LogQLModule, 
ZipkinQueryModule, StatusQueryModule | default providers |
-| **Alarm** | AlarmModule | default |
-| **Telemetry** | TelemetryModule | Prometheus |
-| **Other** | ExporterModule, HealthCheckerModule, AIPipelineModule | default 
providers |
-
-**Full feature set.** Work around issues as they arise.
-
----
-
-## Core Strategy
-
-1. **Build-Time Class Export**: All runtime code generation (OAL via 
Javassist, MAL/LAL via Groovy) runs at build time. Export `.class` files and 
package into native-image classpath. Classpath scanning also runs here as a 
verification gate.
-
-2. **Fixed Module Wiring**: Module/provider selection is hardcoded in this 
distro (no SPI discovery). Simplified config file for selected providers only.
-
-3. **Separation**: SkyWalking upstream changes tracked separately, go through 
upstream PRs.
-
----
-
-## OAL Runtime Class Generation (Javassist)
-
-### 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 at runtime from manifests.
-
-**Details**: [OAL-IMMIGRATION.md](OAL-IMMIGRATION.md)
-
-### 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
-- None. Build-time class export works via existing debug API 
(`setOpenEngineDebug(true)` + `setGeneratedFilePath()`)
-
----
-
-## MAL and LAL (Groovy + Javassist)
-
-### What Happens
-- MAL uses `GroovyShell` + `DelegatingScript` for meter rule expressions 
(~1250 rules across 71 YAML files). 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)
-Run full MAL/LAL initialization at build time via `build-tools/precompiler` 
(unified tool). Export Javassist-generated `.class` files. Transpile all Groovy 
expressions to pure Java at build time — zero Groovy at runtime.
-
-**Details**: [MAL-IMMIGRATION.md](MAL-IMMIGRATION.md) | 
[LAL-IMMIGRATION.md](LAL-IMMIGRATION.md)
-
-### What Was Built
-- **Unified precompiler** (`build-tools/precompiler`): Replaced separate 
`oal-exporter` and `mal-compiler` modules. Compiles all 71 MAL YAML rule files 
(meter-analyzer-config, otel-rules, log-mal-rules, envoy-metrics-rules, 
telegraf-rules, zabbix-rules) producing 1209 meter classes.
-- **MAL-to-Java transpiler**: 1250+ MAL expressions transpiled from Groovy AST 
to pure Java `MalExpression` implementations. 29 filter expressions transpiled 
to `MalFilter` implementations. Zero Groovy at runtime.
-- **LAL-to-Java transpiler**: 10 LAL scripts (6 unique) transpiled to pure 
Java `LalExpression` implementations. Spec classes enhanced with `Consumer` 
overloads for transpiled code.
-- **Groovy stubs module**: Minimal `groovy.lang.*` types (Binding, Closure, 
etc.) for class loading. No `org.codehaus.groovy.*` — prevents GraalVM 
`GroovyIndyInterfaceFeature` from activating.
-- **Manifests**: `META-INF/mal-expressions.txt` (transpiled Java classes), 
`META-INF/mal-groovy-expression-hashes.txt` (SHA-256 for combination pattern 
resolution), `META-INF/mal-meter-classes.txt` (Javassist-generated classes), 
`META-INF/lal-expressions.txt` (transpiled LAL classes), 
`META-INF/annotation-scan/MeterFunction.txt` (16 function classes).
-- **Combination pattern**: Multiple YAML files from different data sources 
(otel, telegraf, zabbix) may define metrics with the same name. Deterministic 
suffixes (`_1`, `_2`) with expression hash tracking enable unambiguous 
resolution.
-- **Same-FQCN replacements**: `DSL.java` (MAL), `DSL.java` (LAL), 
`FilterExpression.java`, `MeterSystem.java`, `Expression.java`, 
`SampleFamily.java` — all use pure Java, no Groovy.
-- **Comparison test suite**: 73 MAL test classes (1281 assertions) + 5 LAL 
test classes (19 assertions) covering all 79 YAML files. Tests require data 
flow through full pipeline (no vacuous agreements). Dual-path: fresh Groovy 
compilation (Path A) vs transpiled Java (Path B).
-
-### Groovy Elimination
-- MAL: `MalExpression` interface replaces `DelegatingScript`. `SampleFamily` 
uses Java functional interfaces (`TagFunction`, `SampleFilter`, 
`ForEachFunction`, `DecorateFunction`, `PropertiesExtractor`) instead of 
`groovy.lang.Closure`.
-- LAL: `LalExpression` interface replaces `DelegatingScript`. Spec classes 
have `Consumer` overloads.
-- No `groovy.lang.Closure` in any production source code. Groovy is test-only 
dependency.
-
----
-
-## Classpath Scanning (Guava ClassPath)
-
-### What Happens
-`ClassPath.from()` used in `SourceReceiverImpl.scan()`, `AnnotationScan`, 
`MeterSystem`, `DefaultMetricsFunctionRegistry`, `FilterMatchers`, 
`MetricsHolder`.
-
-### 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.
-- `MeterSystem` replaced with same-FQCN class that reads from 
`META-INF/annotation-scan/MeterFunction.txt` manifest (16 meter function 
classes). Solved as part of MAL immigration.
-
----
-
-## Module System & Configuration
-
-### Current Behavior
-`ModuleManager` uses `ServiceLoader` (SPI). `application.yml` selects 
providers. Config loaded via reflection (`Field.setAccessible` + `field.set` in 
`YamlConfigLoaderUtils.copyProperties`).
-
-### Approach (this repo)
-1. **New module manager**: Directly constructs chosen 
`ModuleDefine`/`ModuleProvider` — no SPI
-2. **Simplified config file**: Only knobs for selected providers
-3. **Config loading**: **No reflection.** Build-time tool scans all 
`ModuleConfig` subclass fields → generates same-FQCN replacement of 
`YamlConfigLoaderUtils` that uses Lombok setters and VarHandle to set config 
fields directly. Eliminates `Field.setAccessible`/`field.set` and the need for 
`reflect-config.json` for config classes.
-
-**Details**: [CONFIG-INIT-IMMIGRATION.md](CONFIG-INIT-IMMIGRATION.md)
-
-### What Was Built
-- `FixedModuleManager` — direct module/provider construction via 
`ModuleDefine.prepare()` overload, no SPI
-- `GraalVMOAPServerStartUp` — entry point with `configuration.has()` guards 
for 6 optional modules
-- `application.yml` — simplified config for selected providers
-- `ConfigInitializerGenerator` — build-time tool that scans config classes and 
generates `YamlConfigLoaderUtils` replacement
-- `YamlConfigLoaderUtils` — same-FQCN replacement using type-dispatch + 
setter/VarHandle instead of reflection
-- `ModuleDefine` — same-FQCN replacement (`library-module-for-graalvm`) adding 
`prepare(ModuleManager, ModuleProvider, ...)` overload for direct provider 
wiring without ServiceLoader
-
----
-
-## Same-FQCN Packaging (Repackaged Modules)
-
-### Problem
-
-Same-FQCN replacement classes need to shadow upstream originals. Classpath 
ordering tricks confuse developers and AI tools.
-
-### Solution: Per-JAR Repackaged Modules (`oap-libs-for-graalvm`)
-
-Each upstream JAR that has replacement classes gets a corresponding 
`*-for-graalvm` module under `oap-libs-for-graalvm/`. The module uses 
`maven-shade-plugin` to:
-1. Include only the upstream JAR in the shade
-2. Exclude the specific `.class` files being replaced
-3. Produce a JAR containing: all upstream classes MINUS replaced ones PLUS our 
replacements
-
-`oap-graalvm-server` depends on `*-for-graalvm` JARs instead of originals. 
Original upstream JARs are forced to `provided` scope via 
`<dependencyManagement>` to prevent transitive leakage.
-
-### 23 Same-FQCN Replacement Classes Across 13 Modules
-
-**Non-trivial replacements (load pre-compiled assets from manifests):**
-
-| Module | Replacement Classes | Purpose |
-|---|---|---|
-| `library-module-for-graalvm` | `ModuleDefine` | Add `prepare()` overload for 
direct provider wiring (bypasses ServiceLoader) |
-| `server-core-for-graalvm` | `OALEngineLoaderService`, `AnnotationScan`, 
`SourceReceiverImpl`, `MeterSystem`, `CoreModuleConfig`, 
`HierarchyDefinitionService` | Load from manifests instead of 
Javassist/ClassPath; config with @Setter; Java-backed closures instead of 
GroovyShell |
-| `library-util-for-graalvm` | `YamlConfigLoaderUtils` | Set config fields via 
setter instead of reflection |
-| `meter-analyzer-for-graalvm` | `DSL`, `FilterExpression`, `Rules` | Load 
pre-compiled MAL Groovy scripts from manifest; load rule data from JSON 
config-data manifests |
-| `log-analyzer-for-graalvm` | `DSL`, `LogAnalyzerModuleConfig`, `LALConfigs` 
| Load pre-compiled LAL scripts; config with @Setter; load LAL config data from 
JSON config-data manifests |
-| `agent-analyzer-for-graalvm` | `AnalyzerModuleConfig`, `MeterConfigs` | 
Config with @Setter; load meter config data from JSON config-data manifests |
-
-**Config-only replacements (add `@Setter` for reflection-free config):**
-
-| Module | Replacement Class |
-|---|---|
-| `envoy-metrics-receiver-for-graalvm` | `EnvoyMetricReceiverConfig` |
-| `otel-receiver-for-graalvm` | `OtelMetricReceiverConfig` |
-| `ebpf-receiver-for-graalvm` | `EBPFReceiverModuleConfig` |
-| `aws-firehose-receiver-for-graalvm` | `AWSFirehoseReceiverModuleConfig` |
-| `cilium-fetcher-for-graalvm` | `CiliumFetcherConfig` |
-| `status-query-for-graalvm` | `StatusQueryConfig` |
-| `health-checker-for-graalvm` | `HealthCheckerConfig` |
-
-### No Classpath Ordering Required
-
-No duplicate FQCNs on the classpath. The startup script (`oapService.sh`) uses 
a simple flat classpath. The `oap-graalvm-native` uber JAR also has no FQCN 
conflicts.
-
-### Adding New Replacements
-
-To add a new same-FQCN replacement:
-1. Create a new `*-for-graalvm` module under `oap-libs-for-graalvm/` (or add 
to existing one)
-2. Add the replacement `.java` file with the same FQCN
-3. Configure shade plugin to exclude the original `.class` from the upstream 
JAR
-4. Add the `-for-graalvm` artifact to root `pom.xml` `<dependencyManagement>`
-5. In `oap-graalvm-server/pom.xml`: add the original JAR to 
`<dependencyManagement>` as `provided`, add `-for-graalvm` to `<dependencies>`
-6. Add the original JAR to `distribution.xml` `<excludes>`
-
----
-
-## Additional GraalVM Risks
-
-| Risk | Status | Mitigation |
-|------|--------|------------|
-| **Reflection** (annotations, OAL enricher, HTTP handlers, GraphQL types) | 
SOLVED | Auto-generated by precompiler from manifests; 
`log4j2-reflect-config.json` for Log4j2 plugins |
-| **gRPC / Netty / Armeria** | SOLVED | GraalVM reachability metadata repo 
handles these automatically |
-| **Resource loading** (`ResourceUtils`, config files) | SOLVED | 
`resource-config.json` via tracing agent |
-| **Log4j2** | SOLVED | Console-only `log4j2.xml` avoids RollingFile 
reflection chain; Log4j2 plugin classes in `log4j2-reflect-config.json` |
-| **Kafka client** (for Kafka fetcher) | Untested | Known GraalVM support, may 
need config |
-| **Kubernetes client 6.7.1** (for cluster + config) | Untested | Has GraalVM 
support, may need config at runtime |
-
----
-
-## Distro Resource Files
-
-Upstream `server-starter/src/main/resources/` contains 236 files. They fall 
into
-two categories: files included directly in the distro `config/` directory 
(loaded
-at runtime via file I/O), and files consumed by the precompiler at build time
-(not needed at runtime — their logic is baked into pre-compiled `.class` 
files).
-
-### Directly Included in Distro (`config/`)
-
-These files are loaded at runtime via `ResourceUtils.read()`, `Files.walk()`, 
or
-YAML parsing. No reflection involved — safe for GraalVM native image as-is.
-
-| File / Directory | Count | Loaded By | Purpose |
-|---|---|---|---|
-| `application.yml` | 1 | Custom (distro's own, not upstream) | 
Module/provider config |
-| `bydb.yml` | 1 | `BanyanDBConfigLoader` | BanyanDB storage base config |
-| `bydb-topn.yml` | 1 | `BanyanDBConfigLoader` | BanyanDB TopN aggregation 
config |
-| `log4j2.xml` | 1 | Log4j2 framework | Logging configuration |
-| `alarm-settings.yml` | 1 | `AlarmModuleProvider` via `ResourceUtils.read()` 
| Alarm rules |
-| `component-libraries.yml` | 1 | `ComponentLibraryCatalogService` via 
`ResourceUtils.read()` | Component ID mapping |
-| `endpoint-name-grouping.yml` | 1 | `EndpointNameGroupingRuleWatcher` via 
`ResourceUtils.read()` | Endpoint grouping rules |
-| `gateways.yml` | 1 | `UninstrumentedGatewaysConfig` via 
`ResourceUtils.read()` | Gateway definitions |
-| `hierarchy-definition.yml` | 1 | `HierarchyDefinitionService` via 
`ResourceUtils.read()` | Layer hierarchy |
-| `metadata-service-mapping.yaml` | 1 | `ResourceUtils.read()` | Metadata 
service mapping |
-| `service-apdex-threshold.yml` | 1 | `ApdexThresholdConfig` via 
`ResourceUtils.read()` | APDEX thresholds |
-| `trace-sampling-policy-settings.yml` | 1 | `TraceSamplingPolicyWatcher` via 
`ResourceUtils.read()` | Trace sampling |
-| `ui-initialized-templates/**` | 131 | `UITemplateInitializer` via 
`Files.walk()` | UI dashboard JSON templates |
-| `cilium-rules/**` | 2 | `CiliumFetcherProvider` via 
`ResourceUtils.getPathFiles()` | Cilium flow rules |
-| `openapi-definitions/**` | 1 | `EndpointNameGrouping` via 
`ResourceUtils.getPathFiles()` | OpenAPI grouping definitions |
-
-**Total: 146 files** included in the distro `config/` directory.
-
-### Pre-compiled at Build Time (NOT in distro)
-
-These files are consumed by `build-tools/precompiler` during the build. Their
-expressions, scripts, and metric definitions are compiled into `.class` files
-packaged in JARs. The YAML source files are not needed at runtime.
-
-| Category | Count | Pre-compiled Into | Tool |
-|---|---|---|---|
-| `oal/*.oal` | 9 | ~620 metrics classes + ~620 builders + ~45 dispatchers 
(Javassist) | `OALClassExporter` |
-| `meter-analyzer-config/*.yaml` | 11 | 147 Groovy scripts + Javassist meter 
classes | `MALPrecompiler` |
-| `otel-rules/**/*.yaml` | 55 | 1044 Groovy scripts + Javassist meter classes 
| `MALPrecompiler` |
-| `log-mal-rules/*.yaml` | 2 | 2 Groovy scripts | `MALPrecompiler` |
-| `envoy-metrics-rules/*.yaml` | 2 | 26 Groovy scripts + Javassist meter 
classes | `MALPrecompiler` |
-| `telegraf-rules/*.yaml` | 1 | 20 Groovy scripts + Javassist meter classes | 
`MALPrecompiler` |
-| `zabbix-rules/*.yaml` | 1 | 15 Groovy scripts + Javassist meter classes | 
`MALPrecompiler` |
-| `lal/*.yaml` | 8 | 6 unique `@CompileStatic` Groovy classes | 
`LALPrecompiler` |
-
-**Total: 89 files** consumed at build time, producing ~1285 pre-compiled 
classes
-and ~1254 Groovy scripts stored in JARs.
-
-Additionally, the precompiler serializes parsed config POJOs as JSON manifests 
in
-`META-INF/config-data/` (7 JSON files for meter-analyzer-config, otel-rules,
-envoy-metrics-rules, log-mal-rules, telegraf-rules, zabbix-rules, and lal). 
These
-provide the runtime "wiring" data (metric prefixes, rule names, expression 
lookup
-keys) that replacement loader classes use instead of filesystem YAML access.
-
-### Not Included (upstream-only)
-
-| File | Reason |
-|---|---|
-| `application.yml` (upstream) | Replaced by distro's own simplified 
`application.yml` |
-
----
-
-## Build Workflow
-
-### Build System
-- Maven + Makefile orchestrates building on top of the skywalking submodule
-- GraalVM JDK 25 in CI (`.github/workflows/ci.yml`)
-- JVM-mode starter with fixed module wiring (`FixedModuleManager` + 
`GraalVMOAPServerStartUp`)
-- Simplified config file for selected modules (`application.yml`)
-
-### Build-Time Pre-Compilation
-
-**OAL**: OAL engine exports `.class` files (9 defines, ~620 metrics, ~620 
builders, ~45 dispatchers). 7 annotation/interface manifests. 3 same-FQCN 
replacement classes (`OALEngineLoaderService`, `AnnotationScan`, 
`SourceReceiverImpl`).
-
-**MAL**: Unified precompiler (`build-tools/precompiler`) processes 71 YAML 
files → 1250 expressions transpiled to pure Java `MalExpression` + 1209 
Javassist meter classes. Combination pattern with deterministic suffixes + 
expression hash tracking. Same-FQCN replacements: `DSL.java`, 
`FilterExpression.java`, `MeterSystem.java`, `Expression.java`, 
`SampleFamily.java`. 73 comparison test classes, 1281 assertions (100% YAML 
coverage).
-
-**LAL**: 8 YAML files → 10 rules → 6 unique transpiled Java `LalExpression` 
classes. Same-FQCN `DSL.java` loads via SHA-256 hash lookup. 5 comparison test 
classes, 19 assertions (100% branch coverage).
-
-**Config initialization**: `ConfigInitializerGenerator` generates same-FQCN 
`YamlConfigLoaderUtils` using Lombok setters — zero `Field.setAccessible` at 
runtime.
-
-**Config data serialization**: Precompiler serializes parsed config POJOs to 
`META-INF/config-data/*.json` (7 JSON files). 3 same-FQCN replacement loaders 
(`MeterConfigs`, `Rules`, `LALConfigs`) deserialize from JSON instead of 
filesystem YAML.
-
-**Module system**: `ModuleDefine` replacement with direct `prepare()` overload 
(bypasses ServiceLoader). `GraalVMOAPServerStartUp` with `configuration.has()` 
guards for 6 optional modules.
-
-**Distro resource packaging**: 146 runtime files → distro `config/`, 89 
pre-compiled files → JARs. Assembly descriptor (`distribution.xml`) packages 
runtime config files from upstream.
-
-### Groovy Elimination
-
-- MAL-to-Java transpiler: 1250+ expressions → pure Java `MalExpression` (no 
Groovy MOP/ExpandoMetaClass)
-- LAL-to-Java transpiler: 10 scripts → pure Java `LalExpression` (no 
DelegatingScript)
-- `SampleFamily` Closure parameters → Java functional interfaces (zero 
`groovy.lang.Closure` in production)
-- Groovy stubs module for class loading (no `org.codehaus.groovy.*`)
-- HierarchyDefinitionService: same-FQCN replacement with Java-backed closures
-- Real Groovy (`groovy-5.0.3.jar`) is test-only; 
`groovy-stubs-1.0.0-SNAPSHOT.jar` on runtime classpath
-- 1303 tests require actual data flow (no vacuous empty-result agreements)
-
-### Native Image Build
-
-- `native-maven-plugin` (GraalVM buildtools 0.10.4) in `oap-graalvm-native` 
with `-Pnative` profile
-- `reflect-config.json` auto-generated by precompiler from manifests (OAL, 
MAL, LAL, meter, HTTP handlers, GraphQL types)
-- `log4j2-reflect-config.json` for Log4j2 plugin classes; console-only 
`log4j2.xml` with `SW_LOG_LEVEL` env var
-- gRPC/Netty/Protobuf/Armeria via GraalVM reachability metadata repository
-- Auto-scanned reflection metadata: Armeria HTTP handlers (~19), GraphQL 
resolvers (~32), GraphQL types (~182), config POJOs (8)
-- Native binary: ~203MB, boots to full module init with all HTTP endpoints 
functional
-
-### Native Distro Packaging
-
-- Assembly descriptor (`native-distribution.xml`) packages native binary + 
config files
-- `Dockerfile.native` packages native distro into `debian:bookworm-slim`
-- `docker-compose.yml` with BanyanDB + OAP native services
-- CI pipeline: multi-arch native build (amd64 + arm64) with Docker manifest 
push to GHCR
-
-### Remaining Verification
-- Verify all receiver plugins work (gRPC + HTTP endpoints)
-- Verify all query APIs work (GraphQL, PromQL, LogQL, Zipkin)
-- Verify cluster mode (K8s)
-- Verify alarm module
-- Performance benchmarking vs JVM
-
----
-
-## Upstream Changes Tracker
-
-No upstream changes needed. All GraalVM incompatibilities are resolved in this 
distro via same-FQCN replacement and build-time pre-compilation:
-- OAL: build-time class export works via existing debug API
-- MAL: transpiled to pure Java, bypasses Groovy entirely
-- LAL: transpiled to pure Java, bypasses Groovy entirely
-- Dynamic Groovy MOP: transpiled to pure Java, no ExpandoMetaClass/MOP at 
runtime
diff --git a/LAL-IMMIGRATION.md b/LAL-IMMIGRATION.md
deleted file mode 100644
index d22704f..0000000
--- a/LAL-IMMIGRATION.md
+++ /dev/null
@@ -1,352 +0,0 @@
-# LAL Immigration: Build-Time Pre-Compilation + Groovy Elimination
-
-## Context
-
-LAL (Log Analysis Language) uses Groovy with `@CompileStatic` + 
`LALPrecompiledExtension` for log analysis scripts. At startup, 
`GroovyShell.parse()` compiles each LAL DSL script into a 
`LALDelegatingScript`. GraalVM native image cannot compile Groovy at runtime.
-
-LAL also enforces security constraints via `SecureASTCustomizer` — `while`, 
`do-while`, and `for` loops are disallowed. Branch coverage focuses on 
`if`/`else if`/`else` chains.
-
-**Solution**: Compile all LAL scripts at build time via the unified 
precompiler. Export `.class` files + manifests. At runtime, load pre-compiled 
classes via SHA-256 hash lookup — no Groovy compilation.
-
----
-
-## Rule File Inventory
-
-**8 LAL YAML files, 10 rules, 6 unique pre-compiled classes:**
-
-| File | Rule Name | DSL Features |
-|---|---|---|
-| `default.yaml` | default | Empty sink (trivial passthrough) |
-| `nginx.yaml` | nginx-access-log | `tag()` guard, `text { regexp }`, 
conditional tag extraction |
-| `nginx.yaml` | nginx-error-log | `tag()` guard, `text { regexp }`, timestamp 
with format, `metrics {}` |
-| `mysql-slowsql.yaml` | mysql-slowsql | `json {}`, conditional `slowSql {}` |
-| `pgsql-slowsql.yaml` | pgsql-slowsql | Identical DSL to mysql-slowsql |
-| `redis-slowsql.yaml` | redis-slowsql | Identical DSL to mysql-slowsql |
-| `envoy-als.yaml` | envoy-als | `parsed?.` navigation, conditional `abort 
{}`, tag extraction, `rateLimit` sampler |
-| `envoy-als.yaml` | network-profiling-slow-trace | `json {}`, `tag()` guard, 
`sampledTrace {}` with 3-way if/else chains |
-| `mesh-dp.yaml` | network-profiling-slow-trace | Identical DSL to envoy-als's 
2nd rule |
-| `k8s-service.yaml` | network-profiling-slow-trace | Identical DSL to 
envoy-als's 2nd rule |
-
-**SHA-256 deduplication**: mysql/pgsql/redis share identical DSL (1 class). 
The 3 network-profiling rules share identical DSL (1 class). Total unique 
pre-compiled classes: **6**.
-
----
-
-## Build-Time Compilation
-
-The unified precompiler (`build-tools/precompiler`) handles LAL alongside OAL 
and MAL:
-
-1. Loads all 8 LAL YAML files via `LALConfigs.load()`
-2. For each rule's DSL string, compiles with the same `CompilerConfiguration` 
as upstream:
-   - `@CompileStatic` with `LALPrecompiledExtension` for type checking
-   - `SecureASTCustomizer` disallowing loops
-   - `ImportCustomizer` for `ProcessRegistry`
-   - Script base class: `LALDelegatingScript`
-3. Exports compiled `.class` files to the output directory
-4. Writes three manifest files:
-
-**`META-INF/lal-expressions.txt`** — SHA-256 hash → transpiled Java class 
(used at runtime):
-```
-a1b2c3d4...=org.apache.skywalking.oap.server.core.source.oal.rt.lal.LalExpr_0
-e5f6a7b8...=org.apache.skywalking.oap.server.core.source.oal.rt.lal.LalExpr_1
-...
-```
-
-**`META-INF/lal-scripts-by-hash.txt`** — SHA-256 hash → Groovy class 
(build-time artifact):
-```
-a1b2c3d4...=network_profiling_slow_trace
-...
-```
-
-**`META-INF/lal-scripts.txt`** — rule name → Groovy class (build-time 
artifact):
-```
-default=default
-nginx-access-log=nginx_access_log
-...
-```
-
-The `lal-expressions.txt` manifest is the runtime manifest for the transpiled 
Java expressions.
-The other two manifests are build-time artifacts for verification and 
debugging.
-
----
-
-## Runtime Replacement
-
-**Same-FQCN class**: `oap-graalvm-server/.../log/analyzer/dsl/DSL.java`
-
-Same FQCN as upstream `org.apache.skywalking.oap.log.analyzer.dsl.DSL`. The 
`of()` method:
-
-1. Computes SHA-256 of the DSL string
-2. Loads `META-INF/lal-scripts-by-hash.txt` manifest (lazy, thread-safe, 
cached)
-3. Looks up the pre-compiled class name by hash
-4. `Class.forName(className)` → `newInstance()` → cast to `DelegatingScript`
-5. Creates `FilterSpec`, sets delegate, returns `DSL` instance
-
-No `GroovyShell`, no compilation. The pre-compiled class already contains the 
statically-compiled bytecode with all type checking baked in.
-
----
-
-## Key Difference from MAL
-
-| Aspect | MAL | LAL |
-|---|---|---|
-| Groovy mode | Dynamic (MOP, `propertyMissing`, `ExpandoMetaClass`) | 
`@CompileStatic` with extension |
-| Loop support | No restriction | Loops disallowed (`SecureASTCustomizer`) |
-| Script base class | `DelegatingScript` | `LALDelegatingScript` |
-| Manifest lookup | By metric name | By SHA-256 hash of DSL content |
-| GraalVM risk | High (dynamic Groovy MOP) | Low (statically compiled) |
-
-LAL's `@CompileStatic` compilation means the pre-compiled classes are fully 
statically typed — no runtime metaclass manipulation needed. This makes LAL 
significantly more native-image-friendly than MAL.
-
----
-
-## Comparison Test Suite
-
-**5 test classes, 19 assertions** covering all 8 YAML files and 10 rules.
-
-Each test runs both paths and asserts identical `Binding` state:
-
-```
-              LAL YAML file
-                   |
-             Load rules (name, dsl)
-                   |
-            For each rule's DSL:
-                 /            \
-      Path A (Fresh Groovy)  Path B (Pre-compiled)
-      GroovyShell.parse()    SHA-256 → manifest lookup
-      same CompilerConfig    Class.forName() → newInstance()
-               \                /
-         Create FilterSpec (mocked ModuleManager)
-         script.setDelegate(filterSpec)
-         filterSpec.bind(binding)
-         script.run()
-               \                /
-            Assert identical Binding state
-```
-
-**What is compared after evaluation:**
-- `binding.shouldAbort()` — did `abort {}` fire?
-- `binding.shouldSave()` — log persistence flag
-- `binding.log()` — LogData.Builder state (service, serviceInstance, endpoint, 
layer, timestamp, tags)
-- `binding.metricsContainer()` — SampleFamily objects (for nginx-error-log 
`metrics {}`)
-- `binding.databaseSlowStatement()` — builder state (for slowSql rules)
-- `binding.sampledTraceBuilder()` — builder state (for network-profiling 
sampledTrace)
-
-### Test Classes
-
-| Test Class | YAML File(s) | Tests | Coverage |
-|---|---|---|---|
-| `LALDefaultTest` | default.yaml | 2 | Trivial passthrough + manifest 
verification |
-| `LALNginxTest` | nginx.yaml | 5 | Access log: matching/non-matching tag + 
non-matching regex. Error log: matching/non-matching tag |
-| `LALSlowSqlTest` | mysql/pgsql/redis-slowsql.yaml | 3 | SLOW_SQL tag guard 
(match/skip) + 3-file manifest verification |
-| `LALEnvoyAlsTest` | envoy-als.yaml (1st rule) | 3 | Abort path (low code, no 
flags), non-abort with flags, non-abort with high code |
-| `LALNetworkProfilingTest` | envoy-als/mesh-dp/k8s-service.yaml | 6 | 4 
componentId branches (http/tcp x ssl/no-ssl), LOG_KIND guard, 3-file manifest 
verification |
-
-### Branch Coverage
-
-- **`tag()` guard**: All rules with `if (tag("LOG_KIND") == ...)` tested with 
matching and non-matching values
-- **`abort {}`**: envoy-als tested with conditions that trigger and skip abort
-- **`slowSql {}`**: Tested with SLOW_SQL tag match (block executed) and 
non-match (block skipped)
-- **`sampledTrace {}`**: componentId 4-way if/else chain fully covered 
(HTTP=49, HTTPS=129, TLS=130, TCP=110)
-- **`text { regexp }`**: nginx access log tested with matching and 
non-matching text patterns
-- **`json {}`**: Tested via slowSql and network-profiling rules
-- **`rateLimit` sampler**: envoy-als tested with responseFlags present/absent 
(if/else branch)
-- **`parsed?.` navigation**: envoy-als tested with nested Map traversal
-
----
-
-## Config Data Serialization
-
-At build time, the precompiler serializes parsed LAL config POJOs to a JSON 
manifest at
-`META-INF/config-data/lal.json`. This provides the runtime config data (rule 
names, DSL
-strings, layers) for `LogFilterListener` to create `DSL` instances — without 
requiring
-filesystem access to the original YAML files.
-
-| JSON Manifest | Source Directory | Serialized Type |
-|---|---|---|
-| `lal.json` | `lal/` | `Map<String, LALConfigs>` (filename → configs) |
-
-At runtime, the replacement `LALConfigs.load()` deserializes from this JSON 
file instead
-of reading YAML from the filesystem.
-
----
-
-## Same-FQCN Replacements (LAL)
-
-| Upstream Class | Upstream Location | Replacement Location | What Changed |
-|---|---|---|---|
-| `DSL` (LAL) | `analyzer/log-analyzer/.../dsl/DSL.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Complete rewrite. Loads 
transpiled `LalExpression` from `META-INF/lal-expressions.txt` manifest (keyed 
by SHA-256 hash). No Groovy runtime. |
-| `LogAnalyzerModuleConfig` | 
`analyzer/log-analyzer/.../provider/LogAnalyzerModuleConfig.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `@Setter` at class 
level. Enables reflection-free config loading via Lombok setters. |
-| `LALConfigs` | `analyzer/log-analyzer/.../provider/LALConfigs.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Complete rewrite of static 
`load()` method. Loads pre-compiled LAL config data from 
`META-INF/config-data/{path}.json` instead of filesystem YAML files via 
`ResourceUtils.getPathFiles()`. |
-| `AbstractSpec` | `analyzer/log-analyzer/.../dsl/spec/AbstractSpec.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `abort()` no-arg 
overload for transpiled code. |
-| `FilterSpec` | `analyzer/log-analyzer/.../dsl/spec/filter/FilterSpec.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `Consumer` overloads: 
`json()`, `text(Consumer)`, `extractor(Consumer)`, `sink(Consumer)`, 
`filter(Runnable)`. |
-| `ExtractorSpec` | 
`analyzer/log-analyzer/.../dsl/spec/extractor/ExtractorSpec.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `Consumer` overloads: 
`metrics(Consumer)`, `slowSql(Consumer)`, `sampledTrace(Consumer)`. |
-| `SinkSpec` | `analyzer/log-analyzer/.../dsl/spec/sink/SinkSpec.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `Consumer` overloads: 
`sampler(Consumer)`, `enforcer()`, `dropper()`. |
-| `SamplerSpec` | `analyzer/log-analyzer/.../dsl/spec/sink/SamplerSpec.java` | 
`oap-libs-for-graalvm/log-analyzer-for-graalvm/` | Added `rateLimit(String, 
Consumer)`, `possibility(int, Consumer)` for String-keyed samplers. |
-
-All replacements are repackaged into `log-analyzer-for-graalvm` via 
`maven-shade-plugin` — the original `.class` files are excluded from the shaded 
JAR.
-
----
-
-## Files Created
-
-1. 
**`oap-libs-for-graalvm/log-analyzer-for-graalvm/src/main/java/.../log/analyzer/dsl/DSL.java`**
-   Same-FQCN replacement: loads pre-compiled LAL scripts from manifest via 
SHA-256 hash
-
-2. 
**`oap-graalvm-server/src/test/java/.../graalvm/lal/LALScriptComparisonBase.java`**
-   Abstract base class: ModuleManager mock setup, dual-path compilation, 
Binding state comparison
-
-3. **`oap-graalvm-server/src/test/java/.../graalvm/lal/LALDefaultTest.java`**
-   Tests for default.yaml (2 tests)
-
-4. **`oap-graalvm-server/src/test/java/.../graalvm/lal/LALNginxTest.java`**
-   Tests for nginx.yaml access-log and error-log rules (5 tests)
-
-5. **`oap-graalvm-server/src/test/java/.../graalvm/lal/LALSlowSqlTest.java`**
-   Tests for mysql/pgsql/redis-slowsql.yaml (3 tests)
-
-6. **`oap-graalvm-server/src/test/java/.../graalvm/lal/LALEnvoyAlsTest.java`**
-   Tests for envoy-als.yaml envoy-als rule (3 tests)
-
-7. 
**`oap-graalvm-server/src/test/java/.../graalvm/lal/LALNetworkProfilingTest.java`**
-   Tests for network-profiling-slow-trace rule across 3 files (6 tests)
-
----
-
-## Verification
-
-```bash
-# Build precompiler first (generates LAL classes + manifests)
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal \
-  mvn -pl build-tools/precompiler install -DskipTests
-
-# Run LAL tests only
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal \
-  mvn -pl oap-graalvm-server test \
-  -Dtest="org.apache.skywalking.oap.server.graalvm.lal.*"
-
-# Full build (all tests)
-JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal make build-distro
-```
-
-Expected: 19 comparison tests across 5 test classes, all passing.
-
----
-
-## Pure Java LAL Transpiler
-
-LAL transpilation is complete. All 10 LAL scripts (6 unique after SHA-256 
dedup)
-are transpiled from Groovy AST to pure Java source at build time, compiled to
-`.class` files, and loaded at runtime via `LalExpression` interface — no Groovy
-runtime needed.
-
-**Approach**: Both options from the plan were combined:
-- **Option A (Transpiler)**: `LalToJavaTranspiler` converts Groovy AST to Java 
source
-- **Option B (Groovy Stubs)**: `groovy-stubs` module provides minimal 
`groovy.lang.*`
-  types for class loading (no `org.codehaus.groovy.*`)
-
----
-
-## What Was Built
-
-### 1. LalExpression Interface
-`oap-libs-for-graalvm/log-analyzer-for-graalvm/.../dsl/LalExpression.java`
-
-```java
-@FunctionalInterface
-public interface LalExpression {
-    void execute(FilterSpec filterSpec, Binding binding);
-}
-```
-
-### 2. Groovy Stubs Module
-`oap-libs-for-graalvm/groovy-stubs/` — Minimal stub classes:
-- `groovy.lang.Binding`, `Closure`, `GString`, `GroovyObject`, 
`GroovyObjectSupport`
-- `groovy.lang.Script`, `groovy.util.DelegatingScript`
-- `groovy.lang.DelegatesTo`, `MetaClass`, `MissingPropertyException`, 
`GroovyRuntimeException`
-
-Key: **No `org.codehaus.groovy.*` packages** — prevents GraalVM 
`GroovyIndyInterfaceFeature` from activating.
-
-### 3. Spec Class Consumer Overloads
-Same-FQCN replacements in `oap-libs-for-graalvm/log-analyzer-for-graalvm/`:
-- `AbstractSpec` — `abort()` no-arg
-- `FilterSpec` — `json()` no-arg, `text(Consumer)`, `extractor(Consumer)`, 
`sink(Consumer)`, `filter(Runnable)`
-- `ExtractorSpec` — `metrics(Consumer)`, `slowSql(Consumer)`, 
`sampledTrace(Consumer)`
-- `SinkSpec` — `sampler(Consumer)`, `enforcer()`, `dropper()`
-- `SamplerSpec` — `rateLimit(String, Consumer)`, `possibility(int, Consumer)`
-
-### 4. LalToJavaTranspiler
-`build-tools/precompiler/.../LalToJavaTranspiler.java` (~650 lines)
-
-Groovy AST → Java source transpilation:
-- Statement-based emission with delegation context tracking
-- If/else if/else chains
-- Property access via `getAt()`
-- Cast handling (`as String/Long/Boolean/Integer`)
-- GString interpolation → string concatenation
-- Null-safe navigation (`?.` → ternary null checks)
-- Static method calls (`ProcessRegistry`)
-- Map expression handling (named args)
-- Embedded helper methods (`getAt`, `toLong`, `toInt`, `toBoolean`, `isTruthy`)
-- JavaCompiler batch compilation
-- Manifest writing (`META-INF/lal-expressions.txt`)
-
-### 5. Runtime DSL.java
-Updated `oap-libs-for-graalvm/log-analyzer-for-graalvm/.../dsl/DSL.java`:
-- Loads `LalExpression` from `META-INF/lal-expressions.txt` (not 
`DelegatingScript`)
-- `evaluate()` calls `expression.execute(filterSpec, binding)` (not 
`script.run()`)
-
-### 6. Dual-Path Tests Updated
-`LALScriptComparisonBase.java` updated:
-- Path A: Fresh Groovy compilation → `DelegatingScript.run()`
-- Path B: Transpiled `LalExpression` from manifest → 
`expression.execute(filterSpec, binding)`
-- 19 tests across 5 classes, all passing
-
----
-
-## Generated Code Example
-
-Input (network-profiling-slow-trace, Groovy):
-```groovy
-filter {
-    json{}
-    extractor{
-        if (tag("LOG_KIND") == "NET_PROFILING_SAMPLED_TRACE") {
-            sampledTrace {
-                latency parsed.latency as Long
-                componentId 49  // simplified
-            }
-        }
-    }
-}
-```
-
-Output (LalExpr_0.java):
-```java
-public class LalExpr_0 implements LalExpression {
-    private static Object getAt(Object obj, String key) { ... }
-    private static long toLong(Object obj) { ... }
-
-    @Override
-    public void execute(FilterSpec filterSpec, Binding binding) {
-        filterSpec.json();
-        filterSpec.extractor(ext -> {
-            if 
("NET_PROFILING_SAMPLED_TRACE".equals(filterSpec.tag("LOG_KIND"))) {
-                ext.sampledTrace(st -> {
-                    st.latency(toLong(getAt(binding.parsed(), "latency")));
-                    st.componentId(49);
-                });
-            }
-        });
-    }
-}
-```
-
----
-
-## Groovy Runtime Removal
-
-- `groovy-stubs` wired as runtime dependency
-- Real Groovy (`groovy-5.0.3.jar`) moved to test-only scope
-- Native image builds and boots without `org.codehaus.groovy.*` on classpath
-- `GroovyIndyInterfaceFeature` stays dormant (no `org.codehaus.groovy` 
packages)
diff --git a/MAL-IMMIGRATION.md b/MAL-IMMIGRATION.md
deleted file mode 100644
index 5506e39..0000000
--- a/MAL-IMMIGRATION.md
+++ /dev/null
@@ -1,817 +0,0 @@
-# MAL Immigration: Build-Time Pre-Compilation + Groovy Elimination
-
-## Context
-
-MAL (Meter Analysis Language) and LAL (Log Analysis Language) have three 
GraalVM-incompatible runtime patterns:
-
-1. **MeterSystem ClassPath scanning**: `MeterSystem` constructor uses Guava 
`ClassPath.from()` to discover `@MeterFunction`-annotated classes (16 meter 
functions).
-2. **MeterSystem Javassist dynamic class generation**: `MeterSystem.create()` 
uses Javassist `ClassPool.makeClass()` to create one dynamic meter subclass per 
metric rule at runtime (~1188 rules across all MAL sources). Classes live in 
`org.apache.skywalking.oap.server.core.analysis.meter.dynamic.*`.
-3. **Groovy runtime compilation**: MAL uses `GroovyShell.parse()` with dynamic 
Groovy features. LAL uses `GroovyShell.parse()` with `@CompileStatic`. Both 
compile scripts at startup.
-
-**Key architectural difference from OAL**: MAL initialization is a tightly 
coupled pipeline where Groovy compilation, static analysis, and Javassist 
generation are interleaved:
-
-```
-Rule YAML → MetricConvert constructor → Analyzer.build()
-  → DSL.parse()               [Groovy compilation]
-  → e.parse()                  [static analysis → scopeType, functionName, 
metricType]
-  → meterSystem.create()       [Javassist class generation + 
MetricsStreamProcessor registration]
-```
-
-The build tool must execute this entire chain — Groovy pre-compilation and 
Javassist pre-generation cannot be separated.
-
-**Solution**: Run the full MAL/LAL initialization at build time. Export 
Javassist-generated `.class` files + compiled Groovy script bytecode. At 
runtime, load pre-generated classes from manifests — no ClassPath scanning, no 
Javassist, no Groovy compilation.
-
----
-
-## Rule file inventory
-
-| Source | Path | Loader | Files | Metric Rules |
-|--------|------|--------|-------|--------------|
-| Agent meter | `meter-analyzer-config/` | `MeterConfigs.loadConfig()` | 11 | 
~147 |
-| OTel metrics | `otel-rules/` | `Rules.loadRules()` | 55 | ~1039 |
-| Log MAL | `log-mal-rules/` | `Rules.loadRules()` | 2 | ~2 |
-| LAL scripts | `lal/` | `LALConfigs.load()` | 8 | 10 |
-| **Total** | | | **76** | **~1198** |
-
-Each MAL metric rule generates one Groovy script + one Javassist dynamic meter 
class. Each LAL rule generates one Groovy script.
-
----
-
-## MeterFunction Manifest + Same-FQCN MeterSystem
-
-### Problem
-
-`MeterSystem` constructor (`MeterSystem.java:75-96`) scans the classpath:
-
-```java
-ClassPath classpath = ClassPath.from(MeterSystem.class.getClassLoader());
-ImmutableSet<ClassPath.ClassInfo> classes = 
classpath.getTopLevelClassesRecursive("org.apache.skywalking");
-for (ClassPath.ClassInfo classInfo : classes) {
-    Class<?> functionClass = classInfo.load();
-    if (functionClass.isAnnotationPresent(MeterFunction.class)) {
-        functionRegister.put(metricsFunction.functionName(), functionClass);
-    }
-}
-```
-
-### 16 Meter Function classes
-
-| Function Name | Class | Accept Type |
-|---|---|---|
-| `avg` | `AvgFunction` | `Long` |
-| `avgLabeled` | `AvgLabeledFunction` | `DataTable` |
-| `avgHistogram` | `AvgHistogramFunction` | `BucketedValues` |
-| `avgHistogramPercentile` | `AvgHistogramPercentileFunction` | 
`PercentileArgument` |
-| `latest` | `LatestFunction` | `Long` |
-| `latestLabeled` | `LatestLabeledFunction` | `DataTable` |
-| `max` | `MaxFunction` | `Long` |
-| `maxLabeled` | `MaxLabeledFunction` | `DataTable` |
-| `min` | `MinFunction` | `Long` |
-| `minLabeled` | `MinLabeledFunction` | `DataTable` |
-| `sum` | `SumFunction` | `Long` |
-| `sumLabeled` | `SumLabeledFunction` | `DataTable` |
-| `sumHistogram` | `HistogramFunction` | `BucketedValues` |
-| `sumHistogramPercentile` | `SumHistogramPercentileFunction` | 
`PercentileArgument` |
-| `sumPerMin` | `SumPerMinFunction` | `Long` |
-| `sumPerMinLabeled` | `SumPerMinLabeledFunction` | `DataTable` |
-
-### Approach
-
-1. Extend `OALClassExporter` to scan `@MeterFunction` annotation at build 
time. Write `META-INF/annotation-scan/MeterFunction.txt` with format 
`functionName=FQCN` (one entry per line).
-
-2. Create same-FQCN replacement `MeterSystem` in `oap-graalvm-server` that 
reads function registry from the manifest instead of `ClassPath.from()`:
-
-```java
-// Pseudocode for replacement MeterSystem constructor
-public MeterSystem(ModuleManager manager) {
-    this.manager = manager;
-    this.classPool = ClassPool.getDefault();
-
-    // Read from manifest instead of ClassPath.from()
-    for (String line : 
readManifest("META-INF/annotation-scan/MeterFunction.txt")) {
-        String[] parts = line.split("=", 2);
-        String functionName = parts[0];
-        Class<?> functionClass = Class.forName(parts[1]);
-        functionRegister.put(functionName, (Class<? extends AcceptableValue>) 
functionClass);
-    }
-}
-```
-
----
-
-## Build-Time MAL/LAL Pre-Compilation + MeterSystem Class Generation
-
-**Module**: `build-tools/precompiler` (unified tool)
-
-### MALCompiler.java — main build tool
-
-The tool executes the full initialization pipeline at build time:
-
-#### Initialize infrastructure
-
-```java
-// 1. Initialize scope registry (same as OALClassExporter)
-DefaultScopeDefine.reset();
-AnnotationScan scopeScan = new AnnotationScan();
-scopeScan.registerListener(new DefaultScopeDefine.Listener());
-scopeScan.scan();
-
-// 2. Create ExportingMeterSystem — intercepts Javassist to export .class files
-ExportingMeterSystem meterSystem = new ExportingMeterSystem(outputDir);
-```
-
-#### Load and compile all MAL rules
-
-```java
-// 3. Agent meter rules (meter-analyzer-config/)
-List<MeterConfig> agentConfigs = 
MeterConfigs.loadConfig("meter-analyzer-config", activeFiles);
-for (MeterConfig config : agentConfigs) {
-    new MetricConvert(config, meterSystem);  // triggers: Groovy compile + 
parse + Javassist
-}
-
-// 4. OTel rules (otel-rules/)
-List<Rule> otelRules = Rules.loadRules("otel-rules", enabledOtelRules);
-for (Rule rule : otelRules) {
-    new MetricConvert(rule, meterSystem);
-}
-
-// 5. Log MAL rules (log-mal-rules/)
-List<Rule> logMalRules = Rules.loadRules("log-mal-rules", enabledLogMalRules);
-for (Rule rule : logMalRules) {
-    new MetricConvert(rule, meterSystem);
-}
-```
-
-#### Load and compile all LAL rules
-
-```java
-// 6. LAL rules (lal/)
-List<LALConfigs> lalConfigs = LALConfigs.load("lal", lalFiles);
-for (LALConfig config : flattenedConfigs) {
-    DSL.of(moduleManager, logConfig, config.getDsl());  // Groovy compile with 
@CompileStatic
-}
-```
-
-#### Export bytecode + write manifests
-
-The tool intercepts both Groovy and Javassist class generation to capture 
bytecode:
-
-**Javassist interception** (`ExportingMeterSystem`): Override `create()` to 
call `ctClass.toBytecode()` and write `.class` files to the output directory. 
Also records metadata for the manifest.
-
-**Groovy script capture**: Use Groovy's `CompilationUnit` API or intercept 
`GroovyShell.parse()` to extract compiled script bytecode. Each MAL expression 
compiles to a unique script class (name based on hash or sequential index).
-
-**Manifest files**:
-
-`META-INF/mal-meter-classes.txt` — Javassist-generated meter classes:
-```
-# format: metricsName|scopeId|functionName|dataType|FQCN
-meter_java_agent_created_tracing_context_count|1|sum|java.lang.Long|org.apache.skywalking.oap.server.core.analysis.meter.dynamic.meter_java_agent_created_tracing_context_count
-...
-```
-
-`META-INF/mal-groovy-scripts.txt` — Pre-compiled MAL Groovy scripts:
-```
-# format: metricName=scriptClassName
-meter_java_agent_created_tracing_context_count=org.apache.skywalking.oap.server.core.analysis.meter.script.Script0001
-...
-```
-
-`META-INF/lal-scripts.txt` — Pre-compiled LAL Groovy scripts:
-```
-# format: layer:ruleName=scriptClassName
-GENERAL:default=org.apache.skywalking.oap.server.core.analysis.lal.script.LALScript0001
-NGINX:nginx-access-log=org.apache.skywalking.oap.server.core.analysis.lal.script.LALScript0002
-...
-```
-
-### ExportingMeterSystem — Javassist interception
-
-This is a build-time variant of `MeterSystem` that exports bytecode instead of 
loading classes:
-
-```java
-// Pseudocode
-public class ExportingMeterSystem extends MeterSystem {
-    private final Path outputDir;
-    private final List<MeterClassMetadata> metadata = new ArrayList<>();
-
-    @Override
-    public synchronized <T> void create(String metricsName, String 
functionName,
-                                         ScopeType type, Class<T> dataType) {
-        // Same Javassist logic as upstream MeterSystem.create()
-        CtClass metricsClass = classPool.makeClass(METER_CLASS_PACKAGE + 
className, parentClass);
-        // ... add constructor and createNew() method ...
-
-        // Instead of toClass(), write bytecode to disk
-        byte[] bytecode = metricsClass.toBytecode();
-        Path classFile = outputDir.resolve(packageToPath(METER_CLASS_PACKAGE + 
className) + ".class");
-        Files.createDirectories(classFile.getParent());
-        Files.write(classFile, bytecode);
-
-        // Record metadata for manifest
-        metadata.add(new MeterClassMetadata(metricsName, type.getScopeId(),
-            functionName, dataType.getName(), METER_CLASS_PACKAGE + 
className));
-    }
-}
-```
-
-### Same-FQCN replacement classes
-
-**1. MAL `DSL`** (`oap-graalvm-server/.../meter/analyzer/dsl/DSL.java`)
-
-Same FQCN as `org.apache.skywalking.oap.meter.analyzer.dsl.DSL`. `parse()` 
loads a pre-compiled Groovy script class instead of calling 
`GroovyShell.parse()`:
-
-```java
-// Pseudocode
-public static Expression parse(String metricName, String expression) {
-    // Look up pre-compiled script class from manifest
-    String scriptClassName = lookupScript(metricName);
-    Class<?> scriptClass = Class.forName(scriptClassName);
-    DelegatingScript script = (DelegatingScript) 
scriptClass.getDeclaredConstructor().newInstance();
-
-    // Same CompilerConfiguration setup (imports, security) is baked into the 
pre-compiled class
-    return new Expression(metricName, expression, script);
-}
-```
-
-The `Expression.empower()` method still runs at runtime — it calls 
`setDelegate()` and `ExpandoMetaClass` registration. These are Java API calls 
on the Groovy runtime, not compilation.
-
-**2. `FilterExpression`** 
(`oap-graalvm-server/.../meter/analyzer/dsl/FilterExpression.java`)
-
-Same FQCN. Loads pre-compiled filter closure class instead of 
`GroovyShell.evaluate()`.
-
-**3. LAL `DSL`** (`oap-graalvm-server/.../log/analyzer/dsl/DSL.java`)
-
-Same FQCN as `org.apache.skywalking.oap.log.analyzer.dsl.DSL`. `of()` loads 
pre-compiled LAL script class (already `@CompileStatic`) instead of calling 
`GroovyShell.parse()`:
-
-```java
-// Pseudocode
-public static DSL of(ModuleManager moduleManager, LogAnalyzerModuleConfig 
config, String dsl) {
-    String scriptClassName = lookupLALScript(layer, ruleName);
-    Class<?> scriptClass = Class.forName(scriptClassName);
-    DelegatingScript script = (DelegatingScript) 
scriptClass.getDeclaredConstructor().newInstance();
-    FilterSpec filterSpec = new FilterSpec(moduleManager, config);
-    script.setDelegate(filterSpec);
-    return new DSL(script, filterSpec);
-}
-```
-
-**4. `MeterSystem`** (enhanced from Step 1)
-
-The `create()` method becomes a manifest lookup + `MetricsStreamProcessor` 
registration:
-
-```java
-// Pseudocode
-public synchronized <T> void create(String metricsName, String functionName,
-                                     ScopeType type, Class<T> dataType) {
-    if (meterPrototypes.containsKey(metricsName)) {
-        return; // already registered
-    }
-
-    // Load pre-generated class from classpath
-    MeterClassMetadata meta = lookupMeterClass(metricsName);
-    Class<?> targetClass = Class.forName(meta.fqcn);
-    AcceptableValue prototype = (AcceptableValue) 
targetClass.getDeclaredConstructor().newInstance();
-    meterPrototypes.put(metricsName, new MeterDefinition(type, prototype, 
dataType));
-
-    // Register with stream processor (same as upstream)
-    MetricsStreamProcessor.getInstance().create(
-        manager,
-        new StreamDefinition(metricsName, type.getScopeId(), 
prototype.builder(),
-            MetricsStreamProcessor.class),
-        targetClass
-    );
-}
-```
-
-`buildMetrics()` and `doStreamingCalculation()` work unchanged — they use the 
prototype map.
-
-### MAL Groovy: Why @CompileStatic is NOT possible
-
-MAL expressions rely on three dynamic Groovy features:
-
-1. **`propertyMissing(String)`** (`Expression.java:126`): When an expression 
references `counter` or `jvm_memory_bytes_used`, Groovy calls 
`ExpressionDelegate.propertyMissing(sampleName)` which looks up the sample 
family from a `ThreadLocal<Map<String, SampleFamily>>`. Static compilation 
cannot resolve these properties.
-
-2. **`ExpandoMetaClass` on `Number`** (`Expression.java:104-111`): The 
`empower()` method registers `plus`, `minus`, `multiply`, `div` closures on 
`Number.class` to allow expressions like `100 * server_cpu_seconds`. This is 
runtime metaclass manipulation.
-
-3. **Closure arguments** in DSL methods: Expressions like `.tag({tags -> 
tags.gc = 'young_gc'})` and `.filter({tags -> tags.job_name == 'mysql'})` pass 
Groovy closures with dynamic property access.
-
-**Approach**: Pre-compile using standard dynamic Groovy (same 
`CompilerConfiguration` as upstream — `DelegatingScript` base class, 
`SecureASTCustomizer`, `ImportCustomizer`). The compiled `.class` files contain 
the same bytecode that `GroovyShell.parse()` would produce. At runtime, 
`Class.forName()` + `newInstance()` loads the pre-compiled script, and 
`Expression.empower()` sets up the delegate and `ExpandoMetaClass`.
-
-**Native image solution**: Dynamic Groovy was eliminated entirely via the 
MAL-to-Java transpiler (see below). All MAL expressions are transpiled to pure 
Java at build time — no Groovy MOP, no `ExpandoMetaClass`, no `invokedynamic` 
at runtime.
-
-### LAL Groovy: Already @CompileStatic
-
-LAL already uses `@CompileStatic` with `LALPrecompiledExtension` for type 
checking. The `CompilationUnit` approach works directly. Compiled scripts 
extend `LALDelegatingScript` and are fully statically typed.
-
----
-
-## Verification Tests
-
-### MALCompilerTest (in `build-tools/mal-compiler/src/test/`)
-
-```
-- allRuleYamlFilesLoadable:
-    Verify all 76 rule files exist on classpath
-
-- fullCompilationGeneratesExpectedCounts:
-    Run MALCompiler.main(), verify:
-    - Generated meter .class count > 0 for each rule source
-    - Groovy script .class count matches rule count
-    - All 3 manifest files exist and are non-empty
-
-- manifestEntriesAreWellFormed:
-    Parse manifest files, verify format and field count per line
-```
-
-### MALPrecompiledRegistrationTest (in `oap-graalvm-server/src/test/`)
-
-```
-- meterFunctionManifestMatchesClasspath:
-    Compare manifest against Guava ClassPath scan of @MeterFunction
-    (same pattern as PrecompiledRegistrationTest for OAL)
-
-- all16MeterFunctionsInManifest:
-    Verify all 16 known function names appear
-
-- precompiledMeterClassesLoadable:
-    For each entry in mal-meter-classes.txt:
-    - Class.forName() succeeds
-    - Class extends the correct meter function parent
-
-- precompiledGroovyScriptsLoadable:
-    For each entry in mal-groovy-scripts.txt:
-    - Class.forName() succeeds
-    - Class is assignable to DelegatingScript
-
-- precompiledLALScriptsLoadable:
-    For each entry in lal-scripts.txt:
-    - Class.forName() succeeds
-    - Class is assignable to LALDelegatingScript
-```
-
----
-
-## Config Data Serialization
-
-At build time, the precompiler serializes parsed config POJOs to JSON 
manifests in
-`META-INF/config-data/`. This provides the runtime "wiring" data (metric 
prefixes,
-rule names, expression lookup keys) that connects pre-compiled Groovy scripts 
to
-incoming metrics — without requiring filesystem access to the original YAML 
files.
-
-| JSON Manifest | Source Directory | Serialized Type |
-|---|---|---|
-| `meter-analyzer-config.json` | `meter-analyzer-config/` | `Map<String, 
MeterConfig>` (filename → config) |
-| `otel-rules.json` | `otel-rules/` | `List<Rule>` |
-| `envoy-metrics-rules.json` | `envoy-metrics-rules/` | `List<Rule>` |
-| `log-mal-rules.json` | `log-mal-rules/` | `List<Rule>` |
-| `telegraf-rules.json` | `telegraf-rules/` | `List<Rule>` |
-| `zabbix-rules.json` | `zabbix-rules/` | `List<Rule>` |
-
-At runtime, replacement loader classes (`MeterConfigs`, `Rules`) deserialize 
from
-these JSON files instead of reading YAML from the filesystem. Each logs that 
configs
-are loaded from the pre-compiled distro.
-
----
-
-## Same-FQCN Replacements (MAL)
-
-| Upstream Class | Upstream Location | Replacement Location | What Changed |
-|---|---|---|---|
-| `MeterSystem` | `server-core/.../analysis/meter/MeterSystem.java` | 
`oap-libs-for-graalvm/server-core-for-graalvm/` | Complete rewrite. Reads 
`@MeterFunction` classes from `META-INF/annotation-scan/MeterFunction.txt` 
manifest instead of Guava `ClassPath.from()`. Loads pre-generated Javassist 
meter classes from classpath instead of runtime `ClassPool.makeClass()`. |
-| `DSL` (MAL) | `analyzer/meter-analyzer/.../dsl/DSL.java` | 
`oap-libs-for-graalvm/meter-analyzer-for-graalvm/` | Complete rewrite. Loads 
pre-compiled Groovy `DelegatingScript` classes from 
`META-INF/mal-groovy-scripts.txt` manifest instead of `GroovyShell.parse()` 
runtime compilation. |
-| `FilterExpression` | `analyzer/meter-analyzer/.../dsl/FilterExpression.java` 
| `oap-libs-for-graalvm/meter-analyzer-for-graalvm/` | Complete rewrite. Loads 
pre-compiled Groovy filter closure classes from 
`META-INF/mal-filter-scripts.properties` manifest instead of 
`GroovyShell.evaluate()` runtime compilation. |
-| `Rules` | `analyzer/meter-analyzer/.../prometheus/rule/Rules.java` | 
`oap-libs-for-graalvm/meter-analyzer-for-graalvm/` | Complete rewrite. Loads 
pre-compiled rule data from `META-INF/config-data/{path}.json` instead of 
filesystem YAML files via `ResourceUtils.getPath()` + `Files.walk()`. |
-| `MeterConfigs` | 
`analyzer/agent-analyzer/.../meter/config/MeterConfigs.java` | 
`oap-libs-for-graalvm/agent-analyzer-for-graalvm/` | Complete rewrite. Loads 
pre-compiled meter config data from `META-INF/config-data/{path}.json` instead 
of filesystem YAML files via `ResourceUtils.getPathFiles()`. |
-
-All replacements are repackaged into their respective `-for-graalvm` modules 
via `maven-shade-plugin` — the original `.class` files are excluded from the 
shaded JARs.
-
----
-
-## Files Created
-
-1. **`build-tools/precompiler/`** (unified, replaces separate `oal-exporter` + 
`mal-compiler`)
-   - Main build tool: loads all MAL/LAL rules, runs full initialization 
pipeline, exports .class files + manifests
-
-2. 
**`oap-libs-for-graalvm/server-core-for-graalvm/src/main/java/.../core/analysis/meter/MeterSystem.java`**
-   - Same-FQCN replacement: reads function registry from manifest, loads 
pre-generated classes in `create()`
-
-3. 
**`oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/.../meter/analyzer/dsl/DSL.java`**
-   - Same-FQCN replacement: loads pre-compiled MAL Groovy scripts from manifest
-
-4. 
**`oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/.../meter/analyzer/dsl/FilterExpression.java`**
-   - Same-FQCN replacement: loads pre-compiled filter closures from manifest
-
-5. 
**`oap-graalvm-server/src/test/java/.../graalvm/PrecompiledMALExecutionTest.java`**
-   - Runtime registration and loading tests
-
-6. **`oap-graalvm-server/src/test/java/.../graalvm/mal/`** — 73 comparison 
test classes covering all 71 MAL YAML files
-
-## Key Upstream Files (read-only)
-
-- `MeterSystem.java` — ClassPath scan (`constructor:75-96`) + Javassist class 
gen (`create():180-259`)
-- `DSL.java` (meter-analyzer) — MAL Groovy compilation: `GroovyShell.parse()` 
with `DelegatingScript`, `SecureASTCustomizer`
-- `Expression.java` — Script execution: `DelegatingScript.run()`, 
`ExpandoMetaClass` on Number, `ExpressionDelegate.propertyMissing()`
-- `FilterExpression.java` — Filter closure compilation: 
`GroovyShell.evaluate()`
-- `Analyzer.java` — Initialization chain: `build()` → `DSL.parse()` → 
`e.parse()` → `meterSystem.create()`
-- `MetricConvert.java` — Rule → Analyzer creation: `new MetricConvert(rule, 
meterSystem)` triggers full pipeline
-- `DSL.java` (log-analyzer) — LAL Groovy compilation: `@CompileStatic` + 
`LALPrecompiledExtension`
-- `LALDelegatingScript.java` — LAL script base class: `filter()`, `json()`, 
`text()`, `extractor()`, `sink()`
-- `LogFilterListener.java` — LAL DSL factory: `DSL.of()` for each rule, stores 
in `Map<Layer, Map<String, DSL>>`
-- `Rules.java` — OTel/log-MAL rule loading: `loadRules(path, enabledRules)`
-- `MeterConfigs.java` — Agent meter rule loading: `loadConfig(path, fileNames)`
-- `LALConfigs.java` — LAL rule loading: `load(path, files)`
-- `MeterFunction.java` — Annotation: `@MeterFunction(functionName = "...")` on 
meter function classes
-- `AcceptableValue.java` — Interface all meter functions implement
-- `MeterClassPackageHolder.java` — Package anchor for Javassist-generated 
classes
-
----
-
-## Verification
-
-```bash
-# 1. Build everything
-make build-distro
-
-# 2. Check generated meter classes exist
-ls 
build-tools/precompiler/target/generated-classes/org/apache/skywalking/oap/server/core/analysis/meter/dynamic/
-
-# 3. Check transpiled Java expression classes exist
-ls 
build-tools/precompiler/target/generated-classes/org/apache/skywalking/oap/server/core/source/oal/rt/mal/
-
-# 4. Check manifest files
-wc -l 
build-tools/precompiler/target/generated-classes/META-INF/mal-meter-classes.txt
-wc -l 
build-tools/precompiler/target/generated-classes/META-INF/mal-expressions.txt
-wc -l 
build-tools/precompiler/target/generated-classes/META-INF/lal-expressions.txt
-
-# 5. Check MeterFunction manifest
-cat 
build-tools/precompiler/target/generated-classes/META-INF/annotation-scan/MeterFunction.txt
-
-# 6. Verify tests pass
-make build-distro  # runs all comparison tests (1300+ assertions)
-```
-
----
-
-## Pure Java MAL Transpiler
-
-### Why: Groovy + GraalVM Native Image is Incompatible
-
-The first native-image build (`make native-image`) failed with:
-
-```
-Error: Could not find target method:
-  protected static void com.oracle.svm.polyglot.groovy
-    
.Target_org_codehaus_groovy_vmplugin_v7_IndyInterface_invalidateSwitchPoints
-    .invalidateSwitchPoints()
-```
-
-**Root cause**: GraalVM's built-in `GroovyIndyInterfaceFeature` (in 
`library-support.jar`)
-targets `IndyInterface.invalidateSwitchPoints()` — a method removed in Groovy 
5.0.3.
-The Groovy 5.x runtime still uses `org.codehaus.groovy` packages and 
`invokedynamic`
-bootstrapping, but the internal API changed. GraalVM's substitution layer 
cannot find
-the target method.
-
-**Why fixing Groovy is not worth it**: Even if the substitution were patched, 
Groovy's
-dynamic runtime (ExpandoMetaClass, MOP, IndyInterface, MetaClassRegistry) 
requires
-extensive GraalVM reflection/substitution configuration. The MAL pre-compiled 
scripts
-use `invokedynamic` + Groovy MOP for method resolution. Making this work 
reliably in
-native-image would be fragile and complex.
-
-**Solution**: Eliminate Groovy from runtime entirely. Generate pure Java code 
at build
-time that implements the same MAL expression logic. The existing 1303 UTs 
validate
-that the generated Java code produces identical results to the Groovy-compiled 
scripts.
-
----
-
-### Approach: MAL-to-Java Transpiler
-
-**Key insight**: MAL expressions are method chains on `SampleFamily` with a 
well-defined
-API. The precompiler already parses all 1250+ MAL rules. Instead of compiling 
them to
-Groovy bytecode (which needs Groovy runtime), we parse the Groovy AST at build 
time and
-generate equivalent Java source code. Zero Groovy at runtime.
-
-### What the Transpiler Changes
-
-| Aspect | Before (Groovy pre-compilation) | After (Java transpiler) |
-|--------|----------------------------------|--------------------------|
-| Build output | Groovy `.class` bytecode | Pure Java `.class` files |
-| Runtime dependency | Groovy runtime (MOP, ExpandoMetaClass) | None |
-| Expression execution | `DelegatingScript.run()` + `ExpandoMetaClass` | 
`MalExpression.run(Map<String, SampleFamily>)` |
-| Closure parameters | `groovy.lang.Closure` | Java functional interfaces |
-| `propertyMissing()` | Groovy MOP resolves metric names | 
`samples.get("metricName")` |
-| `Number * SampleFamily` | `ExpandoMetaClass` on `Number` | 
`sf.multiply(number)` (transpiler swaps operands) |
-| Filter expressions | Groovy `Script.run()` → `Closure<Boolean>` | 
`SampleFilter.test(Map<String, String>)` |
-
-### What Stays the Same
-
-- **MeterSystem Javassist generation**: Pre-compiled at build time, unchanged
-- **Config data serialization**: JSON manifests for rule configs, unchanged
-- **Manifest-based loading**: Same pattern, new manifest format for Java 
expressions
-- **MetricConvert pipeline**: Still runs at build time for Javassist class 
generation
-
----
-
-### Java Functional Interfaces for Closure Replacements
-
-5 `SampleFamily` methods accept `groovy.lang.Closure` parameters. Replace with 
Java
-functional interfaces:
-
-| Method | Closure Type | Java Interface |
-|--------|-------------|----------------|
-| `tag(Closure<?> cl)` | `Map<String,String> → Map<String,String>` | 
`TagFunction extends Function<Map, Map>` |
-| `filter(Closure<Boolean> f)` | `Map<String,String> → Boolean` | 
`SampleFilter extends Predicate<Map>` |
-| `forEach(List, Closure each)` | `(String, Map) → void` | `ForEachFunction 
extends BiConsumer<String, Map>` |
-| `decorate(Closure c)` | `MeterEntity → void` | `DecorateFunction extends 
Consumer<MeterEntity>` |
-| `instance(..., Closure extractor)` | `Map<String,String> → 
Map<String,String>` | `PropertiesExtractor extends Function<Map, Map>` |
-
-**Files created**:
-- `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/MalExpression.java`
-- 
`oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/SampleFamilyFunctions.java`
-
-### MalExpression Interface
-
-```java
-public interface MalExpression {
-    SampleFamily run(Map<String, SampleFamily> samples);
-}
-```
-
-Each generated MAL expression class implements this interface. The `samples` 
map
-replaces Groovy's `propertyMissing()` delegate — metric names are resolved via
-`samples.get("metricName")`.
-
-### MalFilter Interface
-
-```java
-public interface MalFilter {
-    boolean test(Map<String, String> tags);
-}
-```
-
-Each generated filter expression class implements this interface.
-
----
-
-### Same-FQCN SampleFamily Replacement
-
-**File**: 
`oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/SampleFamily.java`
-
-Copy upstream `SampleFamily.java` and change ONLY the 5 Closure-based method 
signatures:
-
-```java
-// Before (upstream)
-public SampleFamily tag(Closure<?> cl) { ... cl.rehydrate(...).call(arg) ... }
-public SampleFamily filter(Closure<Boolean> filter) { ... 
filter.call(it.labels) ... }
-public SampleFamily forEach(List<String> array, Closure<Void> each) { ... 
each.call(element, labels) ... }
-public SampleFamily decorate(Closure<Void> c) { ... c.call(meterEntity) ... }
-public SampleFamily instance(..., Closure<Map<String,String>> 
propertiesExtractor) { ... }
-
-// After (replacement)
-public SampleFamily tag(TagFunction fn) { ... fn.apply(arg) ... }
-public SampleFamily filter(SampleFilter f) { ... f.test(it.labels) ... }
-public SampleFamily forEach(List<String> array, ForEachFunction each) { ... 
each.accept(element, labels) ... }
-public SampleFamily decorate(DecorateFunction fn) { ... fn.accept(meterEntity) 
... }
-public SampleFamily instance(..., PropertiesExtractor extractor) { ... }
-```
-
-All other methods (tagEqual, sum, avg, histogram, service, endpoint, etc.) 
remain
-identical — they don't use Groovy types.
-
-**Shade config**: Add `SampleFamily.class` and `SampleFamily$*.class` to shade 
excludes.
-
----
-
-### MAL-to-Java Transpiler Implementation
-
-**File**: `build-tools/precompiler/.../MalToJavaTranspiler.java`
-
-Parses MAL expression strings using Groovy's parser (available at build time, 
not needed
-at runtime) and generates pure Java source code.
-
-### Input → Output Example
-
-**Input** (MAL expression string from apisix.yaml):
-```
-apisix_http_status.tagNotEqual('route','').tag({tags->tags.route='route/'+tags['route']})
-  
.sum(['code','service_name','route','node']).rate('PT1M').endpoint(['service_name'],['route'],
 Layer.APISIX)
-```
-
-**Output** (generated Java class):
-```java
-package org.apache.skywalking.oap.server.core.source.oal.rt.mal;
-
-import java.util.*;
-import org.apache.skywalking.oap.meter.analyzer.dsl.*;
-import org.apache.skywalking.oap.server.core.analysis.Layer;
-
-public class MalExpr_meter_apisix_endpoint_http_status implements 
MalExpression {
-    @Override
-    public SampleFamily run(Map<String, SampleFamily> samples) {
-        SampleFamily v0 = samples.get("apisix_http_status");
-        if (v0 == null) return SampleFamily.EMPTY;
-        return v0
-            .tagNotEqual("route", "")
-            .tag(tags -> { tags.put("route", "route/" + tags.get("route")); 
return tags; })
-            .sum(List.of("code", "service_name", "route", "node"))
-            .rate("PT1M")
-            .endpoint(List.of("service_name"), List.of("route"), Layer.APISIX);
-    }
-}
-```
-
-### AST Node → Java Output Mapping
-
-| Groovy AST Node | Java Output |
-|-----------------|-------------|
-| Bare property access (`metric_name`) | `samples.get("metric_name")` |
-| Method call on SampleFamily | Direct method call (same name) |
-| `Closure` in `.tag()` | Lambda: `tags -> { tags.put(...); return tags; }` |
-| `Closure` in `.filter()` | Lambda: `labels -> booleanExpr` |
-| `Closure` in `.forEach()` | Lambda: `(element, labels) -> { ... }` |
-| `tags.name` inside closure | `tags.get("name")` |
-| `tags.name = val` inside closure | `tags.put("name", val)` |
-| `tags['key']` inside closure | `tags.get("key")` |
-| Binary `SF + SF` | `left.plus(right)` |
-| Binary `SF - SF` | `left.minus(right)` |
-| Binary `SF * SF` | `left.multiply(right)` |
-| Binary `SF / SF` | `left.div(right)` |
-| Binary `Number * SF` | `sf.multiply(number)` (swap operands) |
-| Binary `Number + SF` | `sf.plus(number)` (swap operands) |
-| Binary `Number - SF` | `sf.minus(number).negative()` (swap operands) |
-| Binary `Number / SF` | `sf.newValue(v -> number / v)` (swap operands) |
-| Unary `-SF` | `sf.negative()` |
-| `['a','b','c']` (list literal) | `List.of("a", "b", "c")` |
-| `[50,75,90,95,99]` (int list) | `List.of(50, 75, 90, 95, 99)` |
-| String literal `'foo'` | `"foo"` |
-| Enum constant `Layer.APISIX` | `Layer.APISIX` |
-| Enum constant `DetectPoint.SERVER` | `DetectPoint.SERVER` |
-| `DownsamplingType.LATEST` / `LATEST` | `DownsamplingType.LATEST` |
-| `time()` function | `java.time.Instant.now().getEpochSecond()` |
-| `a?.b` (safe navigation) | `a != null ? a.b : null` |
-| `a ?: b` (elvis) | `a != null ? a : b` |
-
-### Filter Expression Transpilation
-
-Filter expressions are simpler — they are boolean closures:
-
-**Input**: `{ tags -> tags.job_name == 'apisix-monitoring' }`
-
-**Output**:
-```java
-public class MalFilter_apisix implements MalFilter {
-    @Override
-    public boolean test(Map<String, String> tags) {
-        return "apisix-monitoring".equals(tags.get("job_name"));
-    }
-}
-```
-
----
-
-### Same-FQCN Expression.java Replacement
-
-**File**: 
`oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/Expression.java`
-
-Simplified — no DelegatingScript, no ExpandoMetaClass, no ExpressionDelegate:
-
-```java
-public class Expression {
-    private final String metricName;
-    private final String literal;
-    private final MalExpression expression;  // Pure Java
-
-    public Result run(Map<String, SampleFamily> sampleFamilies) {
-        SampleFamily sf = expression.run(sampleFamilies);
-        if (sf == SampleFamily.EMPTY) return Result.fail("EMPTY");
-        return Result.success(sf);
-    }
-
-    // parse() still provides ExpressionParsingContext for static analysis
-    // (scopeType, functionName, etc.) — this is unchanged
-}
-```
-
----
-
-### Updated DSL.java and FilterExpression.java
-
-### DSL.java (already exists, update)
-
-Change from loading `DelegatingScript` to loading `MalExpression`:
-
-```java
-public static Expression parse(String metricName, String expression) {
-    MalExpression malExpr = loadFromManifest(metricName);  // Class.forName + 
newInstance
-    return new Expression(metricName, expression, malExpr);
-}
-```
-
-Manifest: `META-INF/mal-expressions.txt` (replaces `mal-groovy-scripts.txt`):
-```
-meter_apisix_endpoint_http_status=org.apache.skywalking.oap.server.core.source.oal.rt.mal.MalExpr_meter_apisix_endpoint_http_status
-```
-
-### FilterExpression.java (already exists, update)
-
-Change from loading Groovy `Script` → `Closure<Boolean>` to loading 
`MalFilter`:
-
-```java
-public class FilterExpression {
-    private final MalFilter filter;
-
-    public FilterExpression(String literal) {
-        this.filter = loadFromManifest(literal);  // Class.forName + 
newInstance
-    }
-
-    public Map<String, SampleFamily> filter(Map<String, SampleFamily> 
sampleFamilies) {
-        // Apply filter.test(labels) to each sample family
-    }
-}
-```
-
----
-
-### Precompiler Integration
-
-Replace `compileMAL()` in `Precompiler.java` to use the transpiler instead of 
Groovy
-compilation:
-
-```java
-private static void compileMAL(String outputDir, ...) {
-    MalToJavaTranspiler transpiler = new MalToJavaTranspiler(outputDir);
-
-    for (Rule rule : allRules) {
-        for (MetricsRule mr : rule.getMetricsRules()) {
-            String metricName = formatMetricName(rule, mr.getName());
-            String expression = formatExp(rule.getExpPrefix(), 
rule.getExpSuffix(), mr.getExp());
-            transpiler.transpile(metricName, expression);
-        }
-        if (rule.getFilter() != null) {
-            transpiler.transpileFilter(rule.getName(), rule.getFilter());
-        }
-    }
-
-    transpiler.writeManifest();   // mal-expressions.txt
-    transpiler.compileAll();      // javac on generated .java files
-}
-```
-
-The Javassist meter class generation is still run via `MetricConvert` pipeline 
(unchanged
-from Phase 2). Only Groovy compilation is replaced by transpilation.
-
----
-
-### Same-FQCN Replacements (Transpiler)
-
-| Upstream Class | Replacement Location | Change |
-|---|---|---|
-| `SampleFamily` | `meter-analyzer-for-graalvm/` | **New.** Closure parameters 
→ functional interfaces |
-| `Expression` | `meter-analyzer-for-graalvm/` | **New.** No Groovy: uses 
`MalExpression` instead of `DelegatingScript` |
-| `DSL` (MAL) | `meter-analyzer-for-graalvm/` | **Updated.** Loads 
`MalExpression` instead of Groovy `DelegatingScript` |
-| `FilterExpression` | `meter-analyzer-for-graalvm/` | **Updated.** Loads 
`MalFilter` instead of Groovy `Closure<Boolean>` |
-
----
-
-### Files Created (Transpiler)
-
-| File | Purpose |
-|------|---------|
-| `.../dsl/MalExpression.java` | Interface for generated MAL expression 
classes |
-| `.../dsl/MalFilter.java` | Interface for generated MAL filter classes |
-| `.../dsl/SampleFamilyFunctions.java` | Functional interfaces: TagFunction, 
SampleFilter, ForEachFunction, DecorateFunction, PropertiesExtractor |
-| `.../meter-analyzer-for-graalvm/SampleFamily.java` | Same-FQCN: Closure → 
functional interfaces |
-| `.../meter-analyzer-for-graalvm/Expression.java` | Same-FQCN: no Groovy, 
uses MalExpression |
-| `.../precompiler/MalToJavaTranspiler.java` | AST-walking transpiler: Groovy 
AST → Java source |
-| Generated `MalExpr_*.java` files | ~1250 pure Java expression classes |
-| Generated `MalFilter_*.java` files | ~29 pure Java filter classes |
-
-### Files Modified (Transpiler)
-
-| File | Change |
-|------|--------|
-| `Precompiler.java` | `compileMAL()` uses transpiler instead of Groovy 
compilation |
-| `meter-analyzer-for-graalvm/pom.xml` | Add SampleFamily, Expression to shade 
excludes |
-| `meter-analyzer-for-graalvm/DSL.java` | Load MalExpression instead of 
DelegatingScript |
-| `meter-analyzer-for-graalvm/FilterExpression.java` | Load MalFilter instead 
of Groovy Closure |
-
----
-
-### Transpiler Verification
-
-```bash
-# 1. Rebuild with transpiler
-make compile
-
-# 2. Run ALL existing tests (1303 UTs validate identical MAL behavior)
-make test
-
-# 3. Boot JVM distro
-make shutdown && make boot
-
-# 4. Verify no Groovy at runtime (after LAL is also transpiled)
-jar tf 
oap-graalvm-server/target/oap-graalvm-jvm-distro/oap-graalvm-jvm-distro/libs/*.jar
 | grep groovy
-# Should find NOTHING
-
-# 5. Attempt native-image build (no more Groovy substitution error)
-make native-image
-```
diff --git a/OAL-IMMIGRATION.md b/OAL-IMMIGRATION.md
deleted file mode 100644
index 6366bee..0000000
--- a/OAL-IMMIGRATION.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# OAL Build-Time Pre-Compilation
-
-## 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).
-
-**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.
-
----
-
-## Build-Time OAL Class Export Tool
-
-**Module**: `build-tools/precompiler` (originally `build-tools/oal-exporter`, 
merged into unified precompiler)
-
-**Created**: `OALClassExporter.java` — main class that:
-
-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
-
-**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.
-
-### 9 OAL Defines processed
-
-| Define | Config File | Source Package | Catalog |
-|--------|-------------|----------------|---------|
-| `DisableOALDefine` | `oal/disable.oal` | `core.source` | — |
-| `CoreOALDefine` | `oal/core.oal` | `core.source` | — |
-| `JVMOALDefine` | `oal/java-agent.oal` | `core.source` | — |
-| `CLROALDefine` | `oal/dotnet-agent.oal` | `core.source` | — |
-| `BrowserOALDefine` | `oal/browser.oal` | `core.browser.source` | — |
-| `MeshOALDefine` | `oal/mesh.oal` | `core.source` | `ServiceMesh` |
-| `EBPFOALDefine` | `oal/ebpf.oal` | `core.source` | — |
-| `TCPOALDefine` | `oal/tcp.oal` | `core.source` | `EnvoyTCP` |
-| `CiliumOALDefine` | `oal/cilium.oal` | `core.source` | — |
-
-### Generated class packages
-
-- Metrics: 
`org.apache.skywalking.oap.server.core.source.oal.rt.metrics.*Metrics`
-- Builders: 
`org.apache.skywalking.oap.server.core.source.oal.rt.metrics.builder.*MetricsBuilder`
-- Dispatchers: 
`org.apache.skywalking.oap.server.core.source.oal.rt.dispatcher.[catalog].*Dispatcher`
-
----
-
-## Runtime Registration via Same-FQCN Replacement Classes
-
-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.
-
-### 3 replacement classes created:
-
-**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`)
-
-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.
-
-### 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** — annotation manifests replace Guava 
scanning
-
----
-
-## Class Loading and Remaining Scans
-
-### `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 enable runtime `Class.forName()` 
lookup.
-
-### 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` — solved in MAL immigration
-`MeterSystem` uses Guava `ClassPath.from()` to discover meter function classes 
at runtime. Replaced with manifest-based loading. See 
[MAL-IMMIGRATION.md](MAL-IMMIGRATION.md).
-
-### `reflect-config.json`
-GraalVM reflection configuration for `Class.forName()` calls on OAL-generated 
and manifest-listed classes is auto-generated by the precompiler from manifests.
-
----
-
-## Same-FQCN Replacements (OAL)
-
-| Upstream Class | Upstream Location | Replacement Location | What Changed |
-|---|---|---|---|
-| `OALEngineLoaderService` | 
`server-core/.../oal/rt/OALEngineLoaderService.java` | 
`oap-libs-for-graalvm/server-core-for-graalvm/` | Complete rewrite. Loads 
pre-compiled OAL classes from build-time manifests instead of running ANTLR4 + 
FreeMarker + Javassist at runtime. |
-| `AnnotationScan` | `server-core/.../annotation/AnnotationScan.java` | 
`oap-libs-for-graalvm/server-core-for-graalvm/` | Complete rewrite. Reads 
`META-INF/annotation-scan/{name}.txt` manifests instead of Guava 
`ClassPath.from()` scanning. |
-| `SourceReceiverImpl` | `server-core/.../source/SourceReceiverImpl.java` | 
`oap-libs-for-graalvm/server-core-for-graalvm/` | Complete rewrite. Reads 
dispatcher/decorator manifests instead of Guava `ClassPath.from()` scanning. |
-
-All three replacements are repackaged into `server-core-for-graalvm` via 
`maven-shade-plugin` — the original `.class` files are excluded from the shaded 
JAR.
-
----
-
-## Files Created
-
-1. **`build-tools/precompiler/src/main/java/.../Precompiler.java`** (unified, 
originally `oal-exporter`)
-   - Build-time tool: runs 9 OAL defines, exports `.class` files, writes OAL 
manifests + annotation/interface manifests + reflection metadata
-
-2. 
**`oap-libs-for-graalvm/server-core-for-graalvm/src/main/java/.../core/oal/rt/OALEngineLoaderService.java`**
-   - Same-FQCN replacement: loads pre-compiled OAL classes from manifests
-
-3. 
**`oap-libs-for-graalvm/server-core-for-graalvm/src/main/java/.../core/annotation/AnnotationScan.java`**
-   - Same-FQCN replacement: reads annotation manifests instead of Guava 
classpath scanning
-
-4. 
**`oap-libs-for-graalvm/server-core-for-graalvm/src/main/java/.../core/source/SourceReceiverImpl.java`**
-   - Same-FQCN replacement: reads dispatcher/decorator manifests instead of 
Guava classpath scanning
-
-5. **`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
-
-## 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` (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()`
-- `CoreModuleProvider.java:417-421` — `start()` calls `load(DisableOALDefine)` 
then `scan()`
-
----
-
-## Verification
-
-```bash
-# 1. Build everything
-make build-distro
-
-# 2. Check generated classes exist
-ls 
build-tools/precompiler/target/generated-classes/org/apache/skywalking/oap/server/core/source/oal/rt/metrics/
-ls 
build-tools/precompiler/target/generated-classes/org/apache/skywalking/oap/server/core/source/oal/rt/dispatcher/
-
-# 3. Check manifest files
-cat 
build-tools/precompiler/target/generated-classes/META-INF/oal-metrics-classes.txt
-cat 
build-tools/precompiler/target/generated-classes/META-INF/oal-dispatcher-classes.txt
-
-# 4. Check annotation scan manifests
-ls build-tools/precompiler/target/generated-classes/META-INF/annotation-scan/
-
-# 5. Verify tests pass
-make build-distro   # runs PrecompiledRegistrationTest
-```
diff --git a/README.md b/README.md
index 6c2c669..534602e 100644
--- a/README.md
+++ b/README.md
@@ -3,89 +3,31 @@
 
 SkyWalking GraalVM Distro is a re-distribution of the official Apache 
SkyWalking OAP server, targeting GraalVM native image on JDK 25.
 
-## Why a Re-Distribution?
+This distro moves all dynamic code generation (Javassist, Groovy, classpath 
scanning) from runtime to build time, producing a ~203MB native binary with 
full OAP feature set. No upstream source modifications required.
 
-SkyWalking OAP server relies heavily on runtime code generation and dynamic 
class loading — patterns that are fundamentally incompatible with GraalVM 
native image's closed-world assumption. This includes Javassist bytecode 
generation (~1,850 classes), Groovy dynamic compilation (~1,260 scripts), Guava 
classpath scanning, ServiceLoader discovery, and reflection-based configuration.
-
-None of these work in a GraalVM native image out of the box.
-
-This distro moves all dynamic code generation from runtime to build time. At 
build time, the full OAL/MAL/LAL initialization pipeline runs, captures all 
generated bytecode, and packages it into the native image classpath. At 
runtime, pre-compiled classes are loaded from manifests — no Javassist, no 
GroovyShell, no classpath scanning.
-
-The **same-FQCN replacement** technique makes this transparent: classes in 
this distro share the exact fully-qualified name with their upstream 
counterparts. Maven classpath ordering ensures the replacement is loaded. No 
forking, no patching.
-
-## Module Selection
-
-This distro targets a **full-feature OAP server** with fixed module/provider 
selection:
-
-- **Storage**: BanyanDB
-- **Cluster**: Standalone, Kubernetes
-- **Configuration**: Kubernetes
-- **Receivers**: All (trace, meter, log, profile, browser, OTel, mesh, envoy, 
Zipkin, Zabbix, Telegraf, etc.)
-- **Query**: GraphQL, PromQL, LogQL, Zipkin
-- **Alarm, Telemetry, Exporter**: Enabled
-
-See [DISTRO-POLICY.md](DISTRO-POLICY.md) for the full module table and build 
plan.
-
-## Build
-
-Requires GraalVM JDK 25.
+## Quick Start
 
 ```bash
-# Initialize submodule
-git submodule update --init --recursive
+git clone --recurse-submodules 
https://github.com/apache/skywalking-graalvm-distro.git
+cd skywalking-graalvm-distro
 
-# Init upstream SkyWalking submodule and install to Maven cache (first time 
only)
+# First time: install upstream SkyWalking to Maven cache
 JAVA_HOME=/path/to/graalvm-jdk-25 make init-skywalking
 
-# Compile distro (precompiler + tests + server)
+# Build distro (precompiler + tests + server)
 JAVA_HOME=/path/to/graalvm-jdk-25 make build-distro
 
-# Build native image (requires GraalVM with native-image)
+# Build native image
 JAVA_HOME=/path/to/graalvm-jdk-25 make native-image
 
-# Build native image for Docker on macOS (cross-compiles via Docker)
-make native-image-macos
-
-# Package into Docker image
-make docker-native
-
-# Run with docker-compose (BanyanDB + OAP native)
+# Run with Docker Compose (BanyanDB + OAP native)
 docker compose -f docker/docker-compose.yml up
 ```
 
-## Project Structure
-
-```
-skywalking-graalvm-distro/
-├── skywalking/                    # Git submodule — DO NOT MODIFY
-├── build-tools/
-│   ├── precompiler/               # Build-time OAL + MAL + LAL 
pre-compilation + reflection metadata
-│   └── config-generator/          # Build-time config code generation 
(YamlConfigLoaderUtils)
-├── oap-libs-for-graalvm/          # Per-JAR repackaged modules (same-FQCN 
replacements via shade)
-├── oap-graalvm-server/            # GraalVM-ready OAP server (JVM distro)
-│   └── src/
-│       ├── main/java/             # Same-FQCN replacement classes
-│       └── test/java/             # 1,300+ comparison tests
-├── oap-graalvm-native/            # Native image module (native-maven-plugin 
+ assembly)
-├── docker/
-│   ├── Dockerfile.native          # Runtime image (debian:bookworm-slim + 
native binary)
-│   └── docker-compose.yml         # BanyanDB + OAP native for local testing
-├── DISTRO-POLICY.md               # Build plan with phase tracking
-├── OAL-IMMIGRATION.md             # OAL pre-compilation design
-├── MAL-IMMIGRATION.md             # MAL pre-compilation design
-├── LAL-IMMIGRATION.md             # LAL pre-compilation design
-└── CONFIG-INIT-IMMIGRATION.md     # Config initialization design
-```
-
 ## Documentation
 
-| Document | Description |
-|---|---|
-| [DISTRO-POLICY.md](DISTRO-POLICY.md) | Build plan, module selection, phase 
tracking, risk assessment |
-| [OAL-IMMIGRATION.md](OAL-IMMIGRATION.md) | OAL (Observability Analysis 
Language) pre-compilation: Javassist class export, annotation scanning, runtime 
manifests |
-| [MAL-IMMIGRATION.md](MAL-IMMIGRATION.md) | MAL (Meter Analysis Language) 
pre-compilation: Groovy-to-Java transpilation, Javassist meter classes, 
combination pattern |
-| [LAL-IMMIGRATION.md](LAL-IMMIGRATION.md) | LAL (Log Analysis Language) 
pre-compilation: Groovy-to-Java transpilation, SHA-256 manifest lookup |
-| [CONFIG-INIT-IMMIGRATION.md](CONFIG-INIT-IMMIGRATION.md) | Config 
initialization: reflection-free config loading via generated code |
+Full documentation is available in [docs/](docs/) and published on the project 
website.
 
 ## License
+
 Apache 2.0
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..eec7fff
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,111 @@
+# SkyWalking GraalVM Distro
+
+**GraalVM native-image distribution of Apache SkyWalking OAP Server.**
+
+This project produces a self-contained native binary of the SkyWalking OAP 
backend.
+It wraps the upstream [Apache 
SkyWalking](https://github.com/apache/skywalking) repository
+as a git submodule and applies build-time transformations to eliminate all 
GraalVM-incompatible
+patterns — without modifying upstream source code.
+
+## Why Native Image
+
+- **Fast startup** — native binary boots directly to full module initialization
+- **Lower memory footprint** — no JIT compiler, no class-loading overhead
+- **Single binary deployment** — ~203MB self-contained executable, ideal for 
containers and cloud-native environments
+
+## How It Works
+
+SkyWalking OAP relies on runtime code generation and dynamic class loading in 
several subsystems.
+This distro moves all of that to build time:
+
+| Subsystem | Runtime Pattern | Build-Time Solution |
+|-----------|----------------|---------------------|
+| **OAL** (metrics) | Javassist generates ~1285 classes at startup | 
`OALClassExporter` runs the OAL engine at build time, exports `.class` files 
and manifests |
+| **MAL** (meters) | Groovy compiles 1250+ expressions at startup | 
`MalToJavaTranspiler` converts Groovy AST to pure Java `MalExpression` classes |
+| **LAL** (logs) | Groovy compiles 10 scripts at startup | 
`LalToJavaTranspiler` converts Groovy AST to pure Java `LalExpression` classes |
+| **Config loading** | `Field.setAccessible()` + reflection | 
`ConfigInitializerGenerator` produces setter-based `YamlConfigLoaderUtils` |
+| **Classpath scanning** | Guava `ClassPath.from()` at startup | Build-time 
manifests under `META-INF/annotation-scan/` |
+| **Module wiring** | ServiceLoader SPI discovery | `FixedModuleManager` with 
hardcoded module/provider construction |
+
+All replacement classes use the **same-FQCN** (fully-qualified class name) 
technique: a replacement
+class with the identical package and name is repackaged via 
`maven-shade-plugin`, excluding the
+original from the upstream JAR. No classpath ordering tricks needed.
+
+## Module Selection
+
+The distro ships with a full feature set. Module/provider selection is fixed 
at build time:
+
+- **Storage**: BanyanDB
+- **Cluster**: Standalone, Kubernetes
+- **Configuration**: Kubernetes
+- **Receivers**: All (trace, JVM, meter, log, browser, OTel, mesh, Envoy, 
Zipkin, Zabbix, Telegraf, AWS Firehose, Cilium, eBPF, async-profiler, pprof, 
CLR, Kafka fetcher)
+- **Analyzers**: Trace, Log, Event
+- **Query**: GraphQL, PromQL, LogQL, Zipkin, Status
+- **Alarm, Telemetry, Exporter, Health Check, AI Pipeline**: All enabled
+
+See [Distribution Policy](distro-policy.md) for the complete module table and 
architecture details.
+
+## Prerequisites
+
+- **GraalVM JDK 25** with `native-image` installed
+- **Maven 3.9+**
+- **Docker** (for container builds and docker-compose)
+
+## Build Commands
+
+```bash
+# Full build (precompiler + tests + JVM distro)
+JAVA_HOME=$GRAALVM_HOME make build-distro
+
+# Precompiler only
+JAVA_HOME=$GRAALVM_HOME mvn -pl build-tools/precompiler install -DskipTests
+
+# Run tests only
+JAVA_HOME=$GRAALVM_HOME mvn -pl oap-graalvm-server test
+
+# Native image
+JAVA_HOME=$GRAALVM_HOME make native-image
+
+# Docker native image (cross-compile on macOS)
+make native-image-macos
+
+# Package into Docker image
+make docker-native
+
+# Run with Docker Compose (BanyanDB + OAP native)
+docker compose -f docker/docker-compose.yml up
+```
+
+## Project Structure
+
+```
+skywalking-graalvm-distro/
+├── skywalking/              # Git submodule — apache/skywalking (read-only)
+├── build-tools/
+│   ├── precompiler/         # OAL + MAL + LAL build-time compilation
+│   └── config-generator/    # Config code generator (YamlConfigLoaderUtils)
+├── oap-libs-for-graalvm/    # Per-JAR same-FQCN replacement modules (shade 
plugin)
+├── oap-graalvm-server/      # GraalVM-ready OAP server (JVM distro)
+├── oap-graalvm-native/      # Native image build (native-maven-plugin)
+├── docker/                  # Dockerfile.native + docker-compose.yml
+└── docs/                    # Documentation
+```
+
+## Test Suites
+
+All build-time transpilations are validated by dual-path comparison tests:
+
+- **MAL**: 73 test classes, 1281 assertions — covers all 71 YAML rule files
+- **LAL**: 5 test classes, 19 assertions — covers all 8 YAML rule files
+
+Each test compiles the expression via both paths (fresh Groovy compilation vs 
pre-compiled Java class)
+and asserts identical results. Tests require actual data flow — no vacuous 
empty-result agreements.
+
+## Further Reading
+
+- [Distribution Policy](distro-policy.md) — full module table, architecture 
constraints, build workflow
+- [Configuration](configuration.md) — all available settings, environment 
variables, and differences from upstream
+- [OAL Pre-Compilation](oal-immigration.md) — Javassist class export, 
annotation scan manifests
+- [MAL Transpilation](mal-immigration.md) — Groovy-to-Java transpiler, 
combination pattern, functional interfaces
+- [LAL Transpilation](lal-immigration.md) — Groovy-to-Java transpiler, SHA-256 
deduplication, spec class overloads
+- [Config Initialization](config-init-immigration.md) — reflection-free config 
loading via generated setters
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..3f9de36
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,628 @@
+# Configuration
+
+The SkyWalking GraalVM Distro uses a simplified `application.yml` with a fixed 
set of modules and providers.
+All settings are configurable via **system environment variables** at runtime, 
following the same
+`${SW_ENV_VAR:default}` pattern as upstream SkyWalking.
+
+For the full upstream configuration reference, see the
+[SkyWalking Configuration 
Vocabulary](https://skywalking.apache.org/docs/main/next/en/setup/backend/configuration-vocabulary/).
+
+## Differences from Upstream
+
+This distro selects a fixed subset of modules/providers at build time. Modules 
and providers **not listed
+below are not available** — they are not compiled into the distro binary.
+
+### Removed Providers (vs upstream)
+
+| Module | Removed Providers | Reason |
+|--------|-------------------|--------|
+| **cluster** | zookeeper, consul, etcd, nacos | Only `standalone` and 
`kubernetes` are included |
+| **storage** | elasticsearch, h2, mysql, postgresql, opensearch | Only 
`banyandb` is included |
+| **configuration** | apollo, consul, etcd, nacos, zookeeper, grpc | Only 
`k8s-configmap` (and `none`) are included |
+| **telemetry** | none (upstream default) | Only `prometheus` is included |
+
+All other modules (receivers, analyzers, query, alarm, exporter, etc.) retain 
their default providers
+with the same configuration options as upstream.
+
+### Optional Modules
+
+Several modules are **disabled by default** (empty selector `:-`). Enable them 
by setting the
+corresponding environment variable:
+
+| Module | Enable Via | Default |
+|--------|-----------|---------|
+| `receiver-zabbix` | `SW_RECEIVER_ZABBIX=default` | disabled |
+| `receiver-zipkin` | `SW_RECEIVER_ZIPKIN=default` | disabled |
+| `query-zipkin` | `SW_QUERY_ZIPKIN=default` | disabled |
+| `kafka-fetcher` | `SW_KAFKA_FETCHER=default` | disabled |
+| `cilium-fetcher` | `SW_CILIUM_FETCHER=default` | disabled |
+| `exporter` | `SW_EXPORTER=default` | disabled |
+
+## Module Configuration Reference
+
+Below is the complete list of modules, their fixed providers, and available 
settings.
+Each setting shows its environment variable and default value.
+
+---
+
+### cluster
+
+**Selector**: `SW_CLUSTER` (default: `standalone`)
+
+Available providers: `standalone`, `kubernetes`
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| *(selector)* | `SW_CLUSTER` | `standalone` |
+
+**kubernetes** provider:
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| namespace | `SW_CLUSTER_K8S_NAMESPACE` | `default` |
+| labelSelector | `SW_CLUSTER_K8S_LABEL` | `app=collector,release=skywalking` |
+| uidEnvName | `SW_CLUSTER_K8S_UID` | `SKYWALKING_COLLECTOR_UID` |
+
+---
+
+### core
+
+**Selector**: `SW_CORE` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| role | `SW_CORE_ROLE` | `Mixed` |
+| restHost | `SW_CORE_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_CORE_REST_PORT` | `12800` |
+| restContextPath | `SW_CORE_REST_CONTEXT_PATH` | `/` |
+| restIdleTimeOut | `SW_CORE_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_CORE_REST_QUEUE_SIZE` | `0` |
+| httpMaxRequestHeaderSize | `SW_CORE_HTTP_MAX_REQUEST_HEADER_SIZE` | `8192` |
+| gRPCHost | `SW_CORE_GRPC_HOST` | `0.0.0.0` |
+| gRPCPort | `SW_CORE_GRPC_PORT` | `11800` |
+| maxConcurrentCallsPerConnection | `SW_CORE_GRPC_MAX_CONCURRENT_CALL` | `0` |
+| maxMessageSize | `SW_CORE_GRPC_MAX_MESSAGE_SIZE` | `52428800` |
+| gRPCThreadPoolSize | `SW_CORE_GRPC_THREAD_POOL_SIZE` | `-1` |
+| gRPCSslEnabled | `SW_CORE_GRPC_SSL_ENABLED` | `false` |
+| gRPCSslKeyPath | `SW_CORE_GRPC_SSL_KEY_PATH` | `""` |
+| gRPCSslCertChainPath | `SW_CORE_GRPC_SSL_CERT_CHAIN_PATH` | `""` |
+| gRPCSslTrustedCAPath | `SW_CORE_GRPC_SSL_TRUSTED_CA_PATH` | `""` |
+| downsampling | *(not overridable)* | `Hour, Day` |
+| enableDataKeeperExecutor | `SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR` | `true` |
+| dataKeeperExecutePeriod | `SW_CORE_DATA_KEEPER_EXECUTE_PERIOD` | `5` |
+| recordDataTTL | `SW_CORE_RECORD_DATA_TTL` | `3` |
+| metricsDataTTL | `SW_CORE_METRICS_DATA_TTL` | `7` |
+| l1FlushPeriod | `SW_CORE_L1_AGGREGATION_FLUSH_PERIOD` | `500` |
+| storageSessionTimeout | `SW_CORE_STORAGE_SESSION_TIMEOUT` | `70000` |
+| persistentPeriod | `SW_CORE_PERSISTENT_PERIOD` | `25` |
+| topNReportPeriod | `SW_CORE_TOPN_REPORT_PERIOD` | `10` |
+| activeExtraModelColumns | `SW_CORE_ACTIVE_EXTRA_MODEL_COLUMNS` | `false` |
+| serviceNameMaxLength | `SW_SERVICE_NAME_MAX_LENGTH` | `70` |
+| serviceCacheRefreshInterval | `SW_SERVICE_CACHE_REFRESH_INTERVAL` | `10` |
+| instanceNameMaxLength | `SW_INSTANCE_NAME_MAX_LENGTH` | `70` |
+| endpointNameMaxLength | `SW_ENDPOINT_NAME_MAX_LENGTH` | `150` |
+| searchableTracesTags | `SW_SEARCHABLE_TAG_KEYS` | 
`http.method,http.status_code,rpc.status_code,db.type,db.instance,mq.queue,mq.topic,mq.broker`
 |
+| searchableLogsTags | `SW_SEARCHABLE_LOGS_TAG_KEYS` | 
`level,http.status_code` |
+| searchableAlarmTags | `SW_SEARCHABLE_ALARM_TAG_KEYS` | `level` |
+| autocompleteTagKeysQueryMaxSize | `SW_AUTOCOMPLETE_TAG_KEYS_QUERY_MAX_SIZE` 
| `100` |
+| autocompleteTagValuesQueryMaxSize | 
`SW_AUTOCOMPLETE_TAG_VALUES_QUERY_MAX_SIZE` | `100` |
+| prepareThreads | `SW_CORE_PREPARE_THREADS` | `2` |
+| enableEndpointNameGroupingByOpenapi | 
`SW_CORE_ENABLE_ENDPOINT_NAME_GROUPING_BY_OPENAPI` | `true` |
+| syncPeriodHttpUriRecognitionPattern | 
`SW_CORE_SYNC_PERIOD_HTTP_URI_RECOGNITION_PATTERN` | `10` |
+| trainingPeriodHttpUriRecognitionPattern | 
`SW_CORE_TRAINING_PERIOD_HTTP_URI_RECOGNITION_PATTERN` | `60` |
+| maxHttpUrisNumberPerService | `SW_CORE_MAX_HTTP_URIS_NUMBER_PER_SVR` | 
`3000` |
+| enableHierarchy | `SW_CORE_ENABLE_HIERARCHY` | `true` |
+| maxHeapMemoryUsagePercent | `SW_CORE_MAX_HEAP_MEMORY_USAGE_PERCENT` | `96` |
+| maxDirectMemoryUsage | `SW_CORE_MAX_DIRECT_MEMORY_USAGE` | `-1` |
+
+---
+
+### storage
+
+**Selector**: `SW_STORAGE` (default: `banyandb`)
+
+Only the `banyandb` provider is available. BanyanDB storage configuration is 
loaded from
+`bydb.yml` and `bydb-topn.yml` at runtime. See the
+[upstream BanyanDB storage 
documentation](https://skywalking.apache.org/docs/main/next/en/setup/backend/storages/banyandb/)
+for details on those files.
+
+---
+
+### agent-analyzer
+
+**Selector**: `SW_AGENT_ANALYZER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| traceSamplingPolicySettingsFile | `SW_TRACE_SAMPLING_POLICY_SETTINGS_FILE` | 
`trace-sampling-policy-settings.yml` |
+| slowDBAccessThreshold | `SW_SLOW_DB_THRESHOLD` | `default:200,mongodb:100` |
+| forceSampleErrorSegment | `SW_FORCE_SAMPLE_ERROR_SEGMENT` | `true` |
+| segmentStatusAnalysisStrategy | `SW_SEGMENT_STATUS_ANALYSIS_STRATEGY` | 
`FROM_SPAN_STATUS` |
+| noUpstreamRealAddressAgents | `SW_NO_UPSTREAM_REAL_ADDRESS` | `6000,9000` |
+| meterAnalyzerActiveFiles | `SW_METER_ANALYZER_ACTIVE_FILES` | 
`datasource,threadpool,satellite,go-runtime,python-runtime,continuous-profiling,java-agent,go-agent,ruby-runtime`
 |
+| slowCacheReadThreshold | `SW_SLOW_CACHE_SLOW_READ_THRESHOLD` | 
`default:20,redis:10` |
+| slowCacheWriteThreshold | `SW_SLOW_CACHE_SLOW_WRITE_THRESHOLD` | 
`default:20,redis:10` |
+
+---
+
+### log-analyzer
+
+**Selector**: `SW_LOG_ANALYZER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| lalFiles | `SW_LOG_LAL_FILES` | 
`envoy-als,mesh-dp,mysql-slowsql,pgsql-slowsql,redis-slowsql,k8s-service,nginx,default`
 |
+| malFiles | `SW_LOG_MAL_FILES` | `nginx` |
+
+---
+
+### event-analyzer
+
+**Selector**: `SW_EVENT_ANALYZER` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-sharing-server
+
+**Selector**: `SW_RECEIVER_SHARING_SERVER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| restHost | `SW_RECEIVER_SHARING_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_RECEIVER_SHARING_REST_PORT` | `0` |
+| restContextPath | `SW_RECEIVER_SHARING_REST_CONTEXT_PATH` | `/` |
+| restIdleTimeOut | `SW_RECEIVER_SHARING_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_RECEIVER_SHARING_REST_QUEUE_SIZE` | `0` |
+| httpMaxRequestHeaderSize | 
`SW_RECEIVER_SHARING_HTTP_MAX_REQUEST_HEADER_SIZE` | `8192` |
+| gRPCHost | `SW_RECEIVER_GRPC_HOST` | `0.0.0.0` |
+| gRPCPort | `SW_RECEIVER_GRPC_PORT` | `0` |
+| maxConcurrentCallsPerConnection | `SW_RECEIVER_GRPC_MAX_CONCURRENT_CALL` | 
`0` |
+| maxMessageSize | `SW_RECEIVER_GRPC_MAX_MESSAGE_SIZE` | `52428800` |
+| gRPCThreadPoolSize | `SW_RECEIVER_GRPC_THREAD_POOL_SIZE` | `0` |
+| gRPCSslEnabled | `SW_RECEIVER_GRPC_SSL_ENABLED` | `false` |
+| gRPCSslKeyPath | `SW_RECEIVER_GRPC_SSL_KEY_PATH` | `""` |
+| gRPCSslCertChainPath | `SW_RECEIVER_GRPC_SSL_CERT_CHAIN_PATH` | `""` |
+| gRPCSslTrustedCAsPath | `SW_RECEIVER_GRPC_SSL_TRUSTED_CAS_PATH` | `""` |
+| authentication | `SW_AUTHENTICATION` | `""` |
+
+---
+
+### receiver-register
+
+**Selector**: `SW_RECEIVER_REGISTER` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-trace
+
+**Selector**: `SW_RECEIVER_TRACE` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-jvm
+
+**Selector**: `SW_RECEIVER_JVM` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-clr
+
+**Selector**: `SW_RECEIVER_CLR` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-profile
+
+**Selector**: `SW_RECEIVER_PROFILE` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-async-profiler
+
+**Selector**: `SW_RECEIVER_ASYNC_PROFILER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| jfrMaxSize | `SW_RECEIVER_ASYNC_PROFILER_JFR_MAX_SIZE` | `31457280` |
+| memoryParserEnabled | `SW_RECEIVER_ASYNC_PROFILER_MEMORY_PARSER_ENABLED` | 
`true` |
+
+---
+
+### receiver-pprof
+
+**Selector**: `SW_RECEIVER_PPROF` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| pprofMaxSize | `SW_RECEIVER_PPROF_MAX_SIZE` | `31457280` |
+| memoryParserEnabled | `SW_RECEIVER_PPROF_MEMORY_PARSER_ENABLED` | `true` |
+
+---
+
+### receiver-zabbix (disabled by default)
+
+**Selector**: `SW_RECEIVER_ZABBIX` (default: disabled)
+
+Set `SW_RECEIVER_ZABBIX=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| port | `SW_RECEIVER_ZABBIX_PORT` | `10051` |
+| host | `SW_RECEIVER_ZABBIX_HOST` | `0.0.0.0` |
+| activeFiles | `SW_RECEIVER_ZABBIX_ACTIVE_FILES` | `agent` |
+
+---
+
+### service-mesh
+
+**Selector**: `SW_SERVICE_MESH` (default: `default`)
+
+No additional settings.
+
+---
+
+### envoy-metric
+
+**Selector**: `SW_ENVOY_METRIC` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| acceptMetricsService | `SW_ENVOY_METRIC_SERVICE` | `true` |
+| enabledEnvoyMetricsRules | `SW_ENVOY_METRIC_ENABLED_RULES` | 
`envoy,envoy-svc-relation` |
+| alsHTTPAnalysis | `SW_ENVOY_METRIC_ALS_HTTP_ANALYSIS` | `""` |
+| alsTCPAnalysis | `SW_ENVOY_METRIC_ALS_TCP_ANALYSIS` | `""` |
+| k8sServiceNameRule | `K8S_SERVICE_NAME_RULE` | 
`${pod.metadata.labels.(service.istio.io/canonical-name)}.${pod.metadata.namespace}`
 |
+| istioServiceNameRule | `ISTIO_SERVICE_NAME_RULE` | 
`${serviceEntry.metadata.name}.${serviceEntry.metadata.namespace}` |
+| istioServiceEntryIgnoredNamespaces | 
`SW_ISTIO_SERVICE_ENTRY_IGNORED_NAMESPACES` | `""` |
+| gRPCHost | `SW_ALS_GRPC_HOST` | `0.0.0.0` |
+| gRPCPort | `SW_ALS_GRPC_PORT` | `0` |
+| maxConcurrentCallsPerConnection | `SW_ALS_GRPC_MAX_CONCURRENT_CALL` | `0` |
+| maxMessageSize | `SW_ALS_GRPC_MAX_MESSAGE_SIZE` | `0` |
+| gRPCThreadPoolSize | `SW_ALS_GRPC_THREAD_POOL_SIZE` | `0` |
+| gRPCSslEnabled | `SW_ALS_GRPC_SSL_ENABLED` | `false` |
+| gRPCSslKeyPath | `SW_ALS_GRPC_SSL_KEY_PATH` | `""` |
+| gRPCSslCertChainPath | `SW_ALS_GRPC_SSL_CERT_CHAIN_PATH` | `""` |
+| gRPCSslTrustedCAsPath | `SW_ALS_GRPC_SSL_TRUSTED_CAS_PATH` | `""` |
+
+---
+
+### kafka-fetcher (disabled by default)
+
+**Selector**: `SW_KAFKA_FETCHER` (default: disabled)
+
+Set `SW_KAFKA_FETCHER=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| bootstrapServers | `SW_KAFKA_FETCHER_SERVERS` | `localhost:9092` |
+| namespace | `SW_NAMESPACE` | `""` |
+| partitions | `SW_KAFKA_FETCHER_PARTITIONS` | `3` |
+| replicationFactor | `SW_KAFKA_FETCHER_PARTITIONS_FACTOR` | `2` |
+| enableNativeProtoLog | `SW_KAFKA_FETCHER_ENABLE_NATIVE_PROTO_LOG` | `true` |
+| enableNativeJsonLog | `SW_KAFKA_FETCHER_ENABLE_NATIVE_JSON_LOG` | `true` |
+| consumers | `SW_KAFKA_FETCHER_CONSUMERS` | `1` |
+| kafkaHandlerThreadPoolSize | `SW_KAFKA_HANDLER_THREAD_POOL_SIZE` | `-1` |
+| kafkaHandlerThreadPoolQueueSize | `SW_KAFKA_HANDLER_THREAD_POOL_QUEUE_SIZE` 
| `-1` |
+
+---
+
+### cilium-fetcher (disabled by default)
+
+**Selector**: `SW_CILIUM_FETCHER` (default: disabled)
+
+Set `SW_CILIUM_FETCHER=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| peerHost | `SW_CILIUM_FETCHER_PEER_HOST` | 
`hubble-peer.kube-system.svc.cluster.local` |
+| peerPort | `SW_CILIUM_FETCHER_PEER_PORT` | `80` |
+| fetchFailureRetrySecond | `SW_CILIUM_FETCHER_FETCH_FAILURE_RETRY_SECOND` | 
`10` |
+| sslConnection | `SW_CILIUM_FETCHER_SSL_CONNECTION` | `false` |
+| sslPrivateKeyFile | `SW_CILIUM_FETCHER_PRIVATE_KEY_FILE_PATH` | *(empty)* |
+| sslCertChainFile | `SW_CILIUM_FETCHER_CERT_CHAIN_FILE_PATH` | *(empty)* |
+| sslCaFile | `SW_CILIUM_FETCHER_CA_FILE_PATH` | *(empty)* |
+| convertClientAsServerTraffic | 
`SW_CILIUM_FETCHER_CONVERT_CLIENT_AS_SERVER_TRAFFIC` | `true` |
+
+---
+
+### receiver-meter
+
+**Selector**: `SW_RECEIVER_METER` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-otel
+
+**Selector**: `SW_OTEL_RECEIVER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| enabledHandlers | `SW_OTEL_RECEIVER_ENABLED_HANDLERS` | 
`otlp-metrics,otlp-logs` |
+| enabledOtelMetricsRules | `SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES` | 
`apisix,nginx/*,k8s/*,istio-controlplane,vm,mysql/*,postgresql/*,oap,aws-eks/*,windows,aws-s3/*,aws-dynamodb/*,aws-gateway/*,redis/*,elasticsearch/*,rabbitmq/*,mongodb/*,kafka/*,pulsar/*,bookkeeper/*,rocketmq/*,clickhouse/*,activemq/*,kong/*,flink/*,banyandb/*`
 |
+
+---
+
+### receiver-zipkin (disabled by default)
+
+**Selector**: `SW_RECEIVER_ZIPKIN` (default: disabled)
+
+Set `SW_RECEIVER_ZIPKIN=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| searchableTracesTags | `SW_ZIPKIN_SEARCHABLE_TAG_KEYS` | `http.method` |
+| sampleRate | `SW_ZIPKIN_SAMPLE_RATE` | `10000` |
+| maxSpansPerSecond | `SW_ZIPKIN_MAX_SPANS_PER_SECOND` | `0` |
+| enableHttpCollector | `SW_ZIPKIN_HTTP_COLLECTOR_ENABLED` | `true` |
+| restHost | `SW_RECEIVER_ZIPKIN_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_RECEIVER_ZIPKIN_REST_PORT` | `9411` |
+| restContextPath | `SW_RECEIVER_ZIPKIN_REST_CONTEXT_PATH` | `/` |
+| restIdleTimeOut | `SW_RECEIVER_ZIPKIN_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_RECEIVER_ZIPKIN_REST_QUEUE_SIZE` | `0` |
+| enableKafkaCollector | `SW_ZIPKIN_KAFKA_COLLECTOR_ENABLED` | `false` |
+| kafkaBootstrapServers | `SW_ZIPKIN_KAFKA_SERVERS` | `localhost:9092` |
+| kafkaGroupId | `SW_ZIPKIN_KAFKA_GROUP_ID` | `zipkin` |
+| kafkaTopic | `SW_ZIPKIN_KAFKA_TOPIC` | `zipkin` |
+| kafkaConsumerConfig | `SW_ZIPKIN_KAFKA_CONSUMER_CONFIG` | 
`{"auto.offset.reset":"earliest","enable.auto.commit":true}` |
+| kafkaConsumers | `SW_ZIPKIN_KAFKA_CONSUMERS` | `1` |
+| kafkaHandlerThreadPoolSize | `SW_ZIPKIN_KAFKA_HANDLER_THREAD_POOL_SIZE` | 
`-1` |
+| kafkaHandlerThreadPoolQueueSize | 
`SW_ZIPKIN_KAFKA_HANDLER_THREAD_POOL_QUEUE_SIZE` | `-1` |
+
+---
+
+### receiver-browser
+
+**Selector**: `SW_RECEIVER_BROWSER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| sampleRate | `SW_RECEIVER_BROWSER_SAMPLE_RATE` | `10000` |
+
+---
+
+### receiver-log
+
+**Selector**: `SW_RECEIVER_LOG` (default: `default`)
+
+No additional settings.
+
+---
+
+### query
+
+**Selector**: `SW_QUERY` (default: `graphql`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| enableLogTestTool | `SW_QUERY_GRAPHQL_ENABLE_LOG_TEST_TOOL` | `false` |
+| maxQueryComplexity | `SW_QUERY_MAX_QUERY_COMPLEXITY` | `3000` |
+| enableUpdateUITemplate | `SW_ENABLE_UPDATE_UI_TEMPLATE` | `false` |
+| enableOnDemandPodLog | `SW_ENABLE_ON_DEMAND_POD_LOG` | `false` |
+
+---
+
+### query-zipkin (disabled by default)
+
+**Selector**: `SW_QUERY_ZIPKIN` (default: disabled)
+
+Set `SW_QUERY_ZIPKIN=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| restHost | `SW_QUERY_ZIPKIN_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_QUERY_ZIPKIN_REST_PORT` | `9412` |
+| restContextPath | `SW_QUERY_ZIPKIN_REST_CONTEXT_PATH` | `/zipkin` |
+| restIdleTimeOut | `SW_QUERY_ZIPKIN_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_QUERY_ZIPKIN_REST_QUEUE_SIZE` | `0` |
+| lookback | `SW_QUERY_ZIPKIN_LOOKBACK` | `86400000` |
+| namesMaxAge | `SW_QUERY_ZIPKIN_NAMES_MAX_AGE` | `300` |
+| uiQueryLimit | `SW_QUERY_ZIPKIN_UI_QUERY_LIMIT` | `10` |
+| uiDefaultLookback | `SW_QUERY_ZIPKIN_UI_DEFAULT_LOOKBACK` | `900000` |
+
+---
+
+### promql
+
+**Selector**: `SW_PROMQL` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| restHost | `SW_PROMQL_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_PROMQL_REST_PORT` | `9090` |
+| restContextPath | `SW_PROMQL_REST_CONTEXT_PATH` | `/` |
+| restIdleTimeOut | `SW_PROMQL_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_PROMQL_REST_QUEUE_SIZE` | `0` |
+| buildInfoVersion | `SW_PROMQL_BUILD_INFO_VERSION` | `2.45.0` |
+| buildInfoRevision | `SW_PROMQL_BUILD_INFO_REVISION` | `""` |
+| buildInfoBranch | `SW_PROMQL_BUILD_INFO_BRANCH` | `""` |
+| buildInfoBuildUser | `SW_PROMQL_BUILD_INFO_BUILD_USER` | `""` |
+| buildInfoBuildDate | `SW_PROMQL_BUILD_INFO_BUILD_DATE` | `""` |
+| buildInfoGoVersion | `SW_PROMQL_BUILD_INFO_GO_VERSION` | `""` |
+
+---
+
+### logql
+
+**Selector**: `SW_LOGQL` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| restHost | `SW_LOGQL_REST_HOST` | `0.0.0.0` |
+| restPort | `SW_LOGQL_REST_PORT` | `3100` |
+| restContextPath | `SW_LOGQL_REST_CONTEXT_PATH` | `/` |
+| restIdleTimeOut | `SW_LOGQL_REST_IDLE_TIMEOUT` | `30000` |
+| restAcceptQueueSize | `SW_LOGQL_REST_QUEUE_SIZE` | `0` |
+
+---
+
+### alarm
+
+**Selector**: `SW_ALARM` (default: `default`)
+
+No additional settings in `application.yml`. Alarm rules are configured via 
`alarm-settings.yml`.
+
+---
+
+### telemetry
+
+**Selector**: `SW_TELEMETRY` (default: `prometheus`)
+
+Only the `prometheus` provider is available.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| host | `SW_TELEMETRY_PROMETHEUS_HOST` | `0.0.0.0` |
+| port | `SW_TELEMETRY_PROMETHEUS_PORT` | `1234` |
+| sslEnabled | `SW_TELEMETRY_PROMETHEUS_SSL_ENABLED` | `false` |
+| sslKeyPath | `SW_TELEMETRY_PROMETHEUS_SSL_KEY_PATH` | `""` |
+| sslCertChainPath | `SW_TELEMETRY_PROMETHEUS_SSL_CERT_CHAIN_PATH` | `""` |
+
+---
+
+### configuration
+
+**Selector**: `SW_CONFIGURATION` (default: `k8s-configmap`)
+
+Available providers: `none`, `k8s-configmap`
+
+**k8s-configmap** provider:
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| period | `SW_CONFIG_CONFIGMAP_PERIOD` | `60` |
+| namespace | `SW_CLUSTER_K8S_NAMESPACE` | `default` |
+| labelSelector | `SW_CLUSTER_K8S_LABEL` | `app=collector,release=skywalking` |
+
+---
+
+### exporter (disabled by default)
+
+**Selector**: `SW_EXPORTER` (default: disabled)
+
+Set `SW_EXPORTER=default` to enable.
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| enableGRPCMetrics | `SW_EXPORTER_ENABLE_GRPC_METRICS` | `false` |
+| gRPCTargetHost | `SW_EXPORTER_GRPC_HOST` | `127.0.0.1` |
+| gRPCTargetPort | `SW_EXPORTER_GRPC_PORT` | `9870` |
+| enableKafkaTrace | `SW_EXPORTER_ENABLE_KAFKA_TRACE` | `false` |
+| enableKafkaLog | `SW_EXPORTER_ENABLE_KAFKA_LOG` | `false` |
+| kafkaBootstrapServers | `SW_EXPORTER_KAFKA_SERVERS` | `localhost:9092` |
+| kafkaProducerConfig | `SW_EXPORTER_KAFKA_PRODUCER_CONFIG` | `""` |
+| kafkaTopicTrace | `SW_EXPORTER_KAFKA_TOPIC_TRACE` | 
`skywalking-export-trace` |
+| kafkaTopicLog | `SW_EXPORTER_KAFKA_TOPIC_LOG` | `skywalking-export-log` |
+| exportErrorStatusTraceOnly | `SW_EXPORTER_KAFKA_TRACE_FILTER_ERROR` | 
`false` |
+
+---
+
+### health-checker
+
+**Selector**: `SW_HEALTH_CHECKER` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| checkIntervalSeconds | `SW_HEALTH_CHECKER_INTERVAL_SECONDS` | `30` |
+
+---
+
+### status-query
+
+**Selector**: `SW_STATUS_QUERY` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| keywords4MaskingSecretsOfConfig | 
`SW_DEBUGGING_QUERY_KEYWORDS_FOR_MASKING_SECRETS` | 
`user,password,token,accessKey,secretKey,authentication` |
+
+---
+
+### configuration-discovery
+
+**Selector**: `SW_CONFIGURATION_DISCOVERY` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| disableMessageDigest | `SW_DISABLE_MESSAGE_DIGEST` | `false` |
+
+---
+
+### receiver-event
+
+**Selector**: `SW_RECEIVER_EVENT` (default: `default`)
+
+No additional settings.
+
+---
+
+### receiver-ebpf
+
+**Selector**: `SW_RECEIVER_EBPF` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| continuousPolicyCacheTimeout | `SW_CONTINUOUS_POLICY_CACHE_TIMEOUT` | `60` |
+| gRPCHost | `SW_EBPF_GRPC_HOST` | `0.0.0.0` |
+| gRPCPort | `SW_EBPF_GRPC_PORT` | `0` |
+| maxConcurrentCallsPerConnection | `SW_EBPF_GRPC_MAX_CONCURRENT_CALL` | `0` |
+| maxMessageSize | `SW_EBPF_ALS_GRPC_MAX_MESSAGE_SIZE` | `0` |
+| gRPCThreadPoolSize | `SW_EBPF_GRPC_THREAD_POOL_SIZE` | `0` |
+| gRPCSslEnabled | `SW_EBPF_GRPC_SSL_ENABLED` | `false` |
+| gRPCSslKeyPath | `SW_EBPF_GRPC_SSL_KEY_PATH` | `""` |
+| gRPCSslCertChainPath | `SW_EBPF_GRPC_SSL_CERT_CHAIN_PATH` | `""` |
+| gRPCSslTrustedCAsPath | `SW_EBPF_GRPC_SSL_TRUSTED_CAS_PATH` | `""` |
+
+---
+
+### receiver-telegraf
+
+**Selector**: `SW_RECEIVER_TELEGRAF` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| activeFiles | `SW_RECEIVER_TELEGRAF_ACTIVE_FILES` | `vm` |
+
+---
+
+### aws-firehose
+
+**Selector**: `SW_RECEIVER_AWS_FIREHOSE` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| host | `SW_RECEIVER_AWS_FIREHOSE_HTTP_HOST` | `0.0.0.0` |
+| port | `SW_RECEIVER_AWS_FIREHOSE_HTTP_PORT` | `12801` |
+| contextPath | `SW_RECEIVER_AWS_FIREHOSE_HTTP_CONTEXT_PATH` | `/` |
+| idleTimeOut | `SW_RECEIVER_AWS_FIREHOSE_HTTP_IDLE_TIME_OUT` | `30000` |
+| acceptQueueSize | `SW_RECEIVER_AWS_FIREHOSE_HTTP_ACCEPT_QUEUE_SIZE` | `0` |
+| maxRequestHeaderSize | 
`SW_RECEIVER_AWS_FIREHOSE_HTTP_MAX_REQUEST_HEADER_SIZE` | `8192` |
+| firehoseAccessKey | `SW_RECEIVER_AWS_FIREHOSE_ACCESS_KEY` | *(empty)* |
+| enableTLS | `SW_RECEIVER_AWS_FIREHOSE_HTTP_ENABLE_TLS` | `false` |
+| tlsKeyPath | `SW_RECEIVER_AWS_FIREHOSE_HTTP_TLS_KEY_PATH` | *(empty)* |
+| tlsCertChainPath | `SW_RECEIVER_AWS_FIREHOSE_HTTP_TLS_CERT_CHAIN_PATH` | 
*(empty)* |
+
+---
+
+### ai-pipeline
+
+**Selector**: `SW_AI_PIPELINE` (default: `default`)
+
+| Setting | Environment Variable | Default |
+|---------|---------------------|---------|
+| uriRecognitionServerAddr | `SW_AI_PIPELINE_URI_RECOGNITION_SERVER_ADDR` | 
*(empty)* |
+| uriRecognitionServerPort | `SW_AI_PIPELINE_URI_RECOGNITION_SERVER_PORT` | 
`17128` |
+| baselineServerAddr | `SW_API_PIPELINE_BASELINE_SERVICE_HOST` | *(empty)* |
+| baselineServerPort | `SW_API_PIPELINE_BASELINE_SERVICE_PORT` | `18080` |
diff --git a/docs/menu.yml b/docs/menu.yml
new file mode 100644
index 0000000..4b0af8b
--- /dev/null
+++ b/docs/menu.yml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+catalog:
+  - name: "Welcome"
+    path: "/readme"
+  - name: "Distribution Policy"
+    path: "/distro-policy"
+  - name: "Configuration"
+    path: "/configuration"
+  - name: "Build-Time Immigration"
+    catalog:
+      - name: "OAL Pre-Compilation"
+        path: "/oal-immigration"
+      - name: "MAL Transpilation"
+        path: "/mal-immigration"
+      - name: "LAL Transpilation"
+        path: "/lal-immigration"
+      - name: "Config Initialization"
+        path: "/config-init-immigration"


Reply via email to