)]}'
{
  "log": [
    {
      "commit": "dae15534d92aad328f86552a11588473d7cac988",
      "tree": "71e314a42f8f15f68434fb772b20de3ccca0d2b8",
      "parents": [
        "3064b5ffa32db675cdce4f568f834ebb0849c806"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Thu Apr 30 19:06:23 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 30 19:06:23 2026 +0800"
      },
      "message": "Fix: LAL compiler treated `(tag(\"x\") as Integer) + (tag(\"y\") as Integer)` as string concatenation instead of numeric addition. (#13857)"
    },
    {
      "commit": "3064b5ffa32db675cdce4f568f834ebb0849c806",
      "tree": "da9a52607737a3148898627a093c761d0e225316",
      "parents": [
        "7754e3ec3646f8fe965507b0d7618cc34c95742e"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Thu Apr 30 13:50:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 30 13:50:09 2026 +0800"
      },
      "message": "Fix: remove the redundant tags from the `envoy-ai-gateway.yaml` LAL configuration. (#13854)"
    },
    {
      "commit": "7754e3ec3646f8fe965507b0d7618cc34c95742e",
      "tree": "c618c8bbfc84db0337c5a5543f461f744734abd5",
      "parents": [
        "36a3f9cb46cfe36f9bbf83ea7731bcbfa3a9cabd"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 30 09:06:35 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 30 09:06:35 2026 +0800"
      },
      "message": "Runtime rule hot-update for MAL and LAL (#13851)\n\nRuntime rule hot-update: REST admin surface for MAL/LAL rule files.\n\nOperators can add, override, inactivate, and delete MAL (`otel-rules`,\n`log-mal-rules`, `telegraf-rules`) and LAL rule files at runtime without\nrestarting OAP. Edits compile and load into the OAP JVM on the fly; every\nnode in a cluster converges on its next periodic scan (~30 s).\n\nThe admin surface is **disabled by default** and listens on **port 17128**\nwhen enabled (`SW_RECEIVER_RUNTIME_RULE\u003ddefault`). It has **no built-in\nauthentication** — operators must gateway-protect with IP allow-lists and\nnever expose it to the public internet.\n\n## REST endpoints\n\n### Write\n\n`POST /runtime/rule/addOrUpdate`\n\n  Body: raw rule YAML. Filter-only edits use a fast in-place swap;\n  structural edits run a cluster pause + DDL + verify + persist + resume\n  cycle.\n\n  Query parameters:\n  - `catalog`               — required; one of `otel-rules`, `log-mal-rules`,\n                              `telegraf-rules`, `lal`. Unknown returns\n                              `400 invalid_catalog`.\n  - `name`                  — required; filesystem-style path under the\n                              catalog root, no extension. Pattern:\n                              `[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*`.\n  - `allowStorageChange`    — optional, default `false`. Set `true` to\n                              permit shape-breaking edits (drops measure\n                              data on BanyanDB; orphans rows on ES/JDBC).\n  - `force`                 — optional, default `false`. Recovery flag;\n                              bypasses the byte-identical no-change\n                              short-circuit so re-pushing known-good content\n                              is treated as a fresh apply.\n\n`POST /runtime/rule/inactivate`\n\n  Soft-pause. Stops dispatching for that rule but preserves the backend\n  measure + history so a later `/addOrUpdate` is lossless. The \"off\" intent\n  is durable across restarts.\n\n  Query parameters:\n  - `catalog`, `name`       — required, same as above.\n\n`POST /runtime/rule/delete`\n\n  Removes an INACTIVE row (ACTIVE rules return\n  `409 requires_inactivate_first`). Behaviour depends on `mode` and whether\n  a bundled YAML twin exists on disk:\n  - default mode + no bundled twin: drops the row, leaves the backend as\n    inert artefact (matches bundled-rule deletion on disk).\n  - default mode + bundled twin: refused with\n    `409 requires_revert_to_bundled` so bundled cannot silently take over\n    without an explicit operator decision.\n  - `?mode\u003drevertToBundled` + bundled twin: schema-change pipeline\n    (install runtime locally, apply bundled through the standard pipeline\n    so the runtime-\u003ebundled delta drops runtime-only metrics, installs\n    bundled-only metrics) before removing the row.\n  - `?mode\u003drevertToBundled` + no bundled twin: returns\n    `400 no_bundled_twin`.\n\n  Query parameters:\n  - `catalog`, `name`       — required.\n  - `mode`                  — optional, default empty. Set\n                              `revertToBundled` to drive the schema-change\n                              pipeline.\n\n### Read\n\n`GET /runtime/rule`\n\n  One rule\u0027s YAML body. Default returns the runtime row; falls back to\n  bundled when the row is absent. Supports `ETag` and `If-None-Match` for\n  cheap 304s.\n\n  Query parameters:\n  - `catalog`, `name`       — required.\n  - `source`                — optional, `runtime` (default) or `bundled`.\n                              `bundled` reads on-disk YAML even when a\n                              runtime override is in place.\n  - HTTP `Accept`           — `application/x-yaml` (default) or\n                              `application/json` for the JSON envelope.\n\n`GET /runtime/rule/bundled`\n\n  Bundled rules in one catalog as JSON, with override flag joined from\n  runtime rows.\n\n  Query parameters:\n  - `catalog`               — required.\n  - `withContent`           — optional, default `true`. When `false`,\n                              omits each YAML body (listing only).\n\n`GET /runtime/rule/list`\n\n  Single JSON envelope `{generatedAt, loaderStats, rules}` merging stored\n  rules with this node\u0027s local state. Each row carries `loaderKind`,\n  `loaderName`, `bundled`, and `bundledContentHash` so a UI can render\n  override badges without a second roundtrip.\n\n  Query parameters:\n  - `catalog`               — optional. Narrows the output to one\n                              catalog. Unknown returns\n                              `400 invalid_catalog`.\n\n`GET /runtime/rule/dump[/\u003ccatalog\u003e]`\n\n  Tar.gz of stored rules + manifest.yaml for backup/DR. Trailing\n  `/\u003ccatalog\u003e` narrows the dump.\n\n### Catalog shortcut routes\n\nMirror the canonical paths for scripts that drive a single catalog:\n- `/runtime/mal/otel/{addOrUpdate,inactivate,delete}` -\u003e `catalog\u003dotel-rules`\n- `/runtime/mal/log/{addOrUpdate,inactivate,delete}`  -\u003e `catalog\u003dlog-mal-rules`\n- `/runtime/lal/{addOrUpdate,inactivate,delete}`      -\u003e `catalog\u003dlal`\n\n`telegraf-rules` is supported via canonical routes only.\n\n## Lifecycle / status of a DSL rule\n\nStatus (DAO row + synthetic):\n- `BUNDLED`  — synthetic, shipped on disk with no operator override. Healthy steady state, no DAO row.\n- `ACTIVE`   — DAO row, runtime override is serving.\n- `INACTIVE` — DAO row, soft-paused tombstone. Handlers torn down; backend preserved.\n- `n/a`      — synthetic transient: row was just removed and this node hasn\u0027t swept yet.\n\nLocal state (per node, transient):\n- `RUNNING`     — dispatching samples; commit complete.\n- `SUSPENDED`   — mid-structural-apply. `suspendOrigin` in {SELF, PEER, BOTH}.\n- `NOT_LOADED`  — after `/inactivate` or never installed; no handlers.\n- (null)        — boot-seeded bundled entry; gone-keys reconcile leaves it alone.\n\nLoader kind (per-file classloader):\n- `RUNTIME` — operator-pushed override active.            Loader prefix `runtime-rule:`.\n- `BUNDLED` — bundled rule served via fall-over loader.   Loader prefix `bundled:`.\n- `NONE`    — no per-file loader (bundled-only via the OAP shared default loader, or INACTIVE).\n\nState matrix:\n\n| Operator action history                  | status     | loaderKind | bundled | What is serving                                                 |\n| ---------------------------------------- | ---------- | ---------- | ------- | --------------------------------------------------------------- |\n| Bundled rule, never touched              | `BUNDLED`  | `NONE`     | `true`  | Bundled YAML, OAP shared default classloader.                   |\n| `/addOrUpdate` overriding bundled        | `ACTIVE`   | `RUNTIME`  | `true`  | Runtime override; compare contentHash vs bundledContentHash.    |\n| `/addOrUpdate` brand-new (no twin)       | `ACTIVE`   | `RUNTIME`  | `false` | Runtime override; no bundled fallback.                          |\n| `/inactivate` of override                | `INACTIVE` | `NONE`     | `true`  | Nothing. Bundled does NOT auto-resurrect.                       |\n| `/inactivate` of bundled-only            | `INACTIVE` | `NONE`     | `true`  | Nothing. Tombstone carries the bundled YAML at inactivate-time. |\n| `/inactivate` of brand-new               | `INACTIVE` | `NONE`     | `false` | Nothing. Rule is off.                                           |\n| Post-`revertToBundled` row removed       | `n/a`      | `BUNDLED`  | `true`  | Bundled rule freshly compiled into a `bundled:` loader.         |\n\nLifecycle transitions (linear form, friendly to plain-text diff views):\n\n1.  Initial state: `BUNDLED` (rule shipped on disk, no DAO row).\n2.  `/addOrUpdate` against a bundled or absent rule  -\u003e  `ACTIVE` (loaderKind\u003dRUNTIME).\n3.  `/addOrUpdate` against an `ACTIVE` rule          -\u003e  `ACTIVE` (re-applies; filter-only fast path or structural pipeline depending on the diff).\n4.  `/inactivate` against `ACTIVE`                   -\u003e  `INACTIVE` (handlers torn down, backend preserved).\n5.  `/inactivate` against `BUNDLED`                  -\u003e  `INACTIVE` (tombstone row carrying the bundled YAML at inactivate time).\n6.  `/inactivate` against `INACTIVE`                 -\u003e  `INACTIVE` (idempotent, returns `200 already_inactive`).\n\nFrom `INACTIVE` there are exactly three legal exits:\n\n7a. `/addOrUpdate` with same content                 -\u003e  `ACTIVE` (reactivate; full structural pipeline).\n7b. `/addOrUpdate` with new content                  -\u003e  `ACTIVE` (reactivate with edits).\n7c. `/delete?mode\u003drevertToBundled`                   -\u003e  row gone, `BUNDLED` loader installed (only if a bundled twin exists on disk).\n\n`/delete` (default mode) on `INACTIVE`:\n8a. No bundled twin on disk                          -\u003e  row gone, backend left as inert artefact.\n8b. Bundled twin on disk                             -\u003e  `409 requires_revert_to_bundled` (refused; operator must opt in via 7c).\n\nConstraints:\n- `/delete` against `ACTIVE` always returns `409 requires_inactivate_first` — destruction goes through the explicit two-step `/inactivate -\u003e /delete` workflow.\n- The `INACTIVE` tombstone is durable across OAP restarts; bundled does NOT auto-resurrect when a runtime override is removed via `/inactivate`. Only path 7c brings bundled back.\n\n## Persistence\n\nHot-updates survive OAP restart: at boot, OAP merges bundled rule files with\npersisted runtime rules so the cluster never silently regresses to bundled\ndefaults.\n\nDAO row shape: `(catalog, name, content, status, updateTime)`. Per-backend\nDAO implementations:\n- BanyanDB — etcd-backed property writes; cluster fences on `mod_revision` via Schema Barrier.\n- Elasticsearch — upsert by row.\n- JDBC (H2 / MySQL / PostgreSQL / TiDB / OceanBase) — upsert by row.\n\n## Configuration\n\nApplication.yml block (`oap-server/server-starter/src/main/resources/application.yml`):\n\n| Knob                       | Env var                                                 | Default          |\n| -------------------------- | ------------------------------------------------------- | ---------------- |\n| selector                   | `SW_RECEIVER_RUNTIME_RULE`                              | empty (disabled) |\n| `restHost`                 | `SW_RECEIVER_RUNTIME_RULE_REST_HOST`                    | `0.0.0.0`        |\n| `restPort`                 | `SW_RECEIVER_RUNTIME_RULE_REST_PORT`                    | `17128`          |\n| `restContextPath`          | `SW_RECEIVER_RUNTIME_RULE_REST_CONTEXT_PATH`            | `/`              |\n| `restIdleTimeOut`          | `SW_RECEIVER_RUNTIME_RULE_REST_IDLE_TIMEOUT`            | `30000`          |\n| `restAcceptQueueSize`      | `SW_RECEIVER_RUNTIME_RULE_REST_QUEUE_SIZE`              | `0`              |\n| `httpMaxRequestHeaderSize` | `SW_RECEIVER_RUNTIME_RULE_HTTP_MAX_REQUEST_HEADER_SIZE` | `8192`           |\n| `reconcilerIntervalSeconds`| `SW_RECEIVER_RUNTIME_RULE_RECONCILER_INTERVAL_SECONDS`  | `30`             |\n| `selfHealThresholdSeconds` | `SW_RECEIVER_RUNTIME_RULE_SELF_HEAL_THRESHOLD_SECONDS`  | `60`             |\n\n## Security\n\n- Disabled by default; `selector` is empty out of the box.\n- The admin port has **no authentication** in this iteration. Operators must\n  gateway-protect with IP allow-lists + auth and never expose port 17128 to\n  the public internet.\n- Audit every request — rule content compiles into the OAP JVM, equivalent\n  to shell access on the OAP host.\n- Cluster Suspend RPC rides the existing OAP cluster-bus gRPC server\n  (RemoteService / HealthCheck transport), separate from port 17128.\n\n## Documentation\n\n- `docs/en/setup/backend/backend-runtime-rule-api.md` — full API reference with applyStatus codes and per-backend `/delete` semantics.\n- `docs/en/concepts-and-designs/runtime-rule-hot-update.md` — design doc.\n- `docs/en/security/README.md` — security notice for the admin surface.\n- `docs/en/setup/backend/configuration-vocabulary.md` — env-var reference."
    },
    {
      "commit": "36a3f9cb46cfe36f9bbf83ea7731bcbfa3a9cabd",
      "tree": "65eaa7b4a583b190d5b97be31a5415ae9936795b",
      "parents": [
        "a06f2e1681d0a1cf76a54858dc949a8ea898f96d"
      ],
      "author": {
        "name": "Fine0830",
        "email": "fanxue0830@gmail.com",
        "time": "Mon Apr 27 19:50:26 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 27 19:50:26 2026 +0800"
      },
      "message": "Sync UI (#13848)"
    },
    {
      "commit": "a06f2e1681d0a1cf76a54858dc949a8ea898f96d",
      "tree": "e6a4df3a03c581a0fa0af402d0465edb629355ef",
      "parents": [
        "2319def4a905862aca21edf7703e36549335e3ea"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Mon Apr 27 12:41:01 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 27 12:41:01 2026 +0800"
      },
      "message": "MAL: add `safeDiv(divisor)` on `SampleFamily` that yields `0` when the divisor is `0`. (#13846)"
    },
    {
      "commit": "2319def4a905862aca21edf7703e36549335e3ea",
      "tree": "a3c8b412c1c690cbdc50e9e74ece30f708a1526f",
      "parents": [
        "8242ff6370a92863ba58fc9231fe9add1cc13778"
      ],
      "author": {
        "name": "weixiang1862",
        "email": "652048614@qq.com",
        "time": "Thu Apr 23 16:16:02 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 23 16:16:02 2026 +0800"
      },
      "message": "Fix potential unexpected current directory inclusion in Docker OAP classpath (#13844)"
    },
    {
      "commit": "8242ff6370a92863ba58fc9231fe9add1cc13778",
      "tree": "57af7ba846bca111853de69996314df37f9b2319",
      "parents": [
        "315defe81613fdfa53b874b9bfda0d8296f1e68c"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Tue Apr 21 18:12:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 18:12:24 2026 +0800"
      },
      "message": "Fix: remove the dependency from `VirtualServiceAnalysisListener` if `GenAIAnalyzerModule` is disabled. (#13838)"
    },
    {
      "commit": "315defe81613fdfa53b874b9bfda0d8296f1e68c",
      "tree": "92149c2824fae20d52a597034b0191f3cecaf75b",
      "parents": [
        "c171fbfdb75cb5ee04761ff872cb2972092e01d2"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Apr 21 15:59:25 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 15:59:25 2026 +0800"
      },
      "message": "Bump skywalking-ui submodule for SWIP-12 i18n menu labels (#13840)"
    },
    {
      "commit": "c171fbfdb75cb5ee04761ff872cb2972092e01d2",
      "tree": "da8415264acfce9cefa3f0729acff71cbd074ec8",
      "parents": [
        "681a5be8cb1d4bb77eb40a0d079391ade439a64c"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Apr 21 15:33:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 15:33:09 2026 +0800"
      },
      "message": "Implement SWIP-12: WeChat \u0026 Alipay Mini Program monitoring (#13835)\n\n## Implement SWIP-12: WeChat \u0026 Alipay Mini Program monitoring (#13835)\n\nAdds end-to-end OAP support for WeChat and Alipay Mini Program observability via the\n[SkyAPM mini-program-monitor](https://github.com/SkyAPM/mini-program-monitor) SDK. Both\nruntimes report through standard OTLP (metrics + logs) plus SkyWalking native trace\nsegments — no new receivers, no new SPIs.\n\n### Highlights\n\n- Two new layers: `WECHAT_MINI_PROGRAM(48)` and `ALIPAY_MINI_PROGRAM(49)`.\n- Two new JavaScript component ids registered in `component-libraries.yml`:\n  `WeChat-MiniProgram(10002)` and `AliPay-MiniProgram(10003)`.\n- Mini-program services / instances / endpoints are produced by **MAL** (OTLP metrics)\n  and **LAL** (OTLP logs) — not by trace analysis. Client-side (exit-only) segments\n  flow through the standard `RPCAnalysisListener` pipeline unchanged, same pattern as\n  browser and iOS layers; `service_cpm` / `service_resp_time` / `endpoint_cpm` do not\n  populate under mini-program layers by design.\n- One MAL enhancement applies generally: `TimeUnit` added to `MALCodegenHelper.ENUM_FQCN`\n  so rule YAML can write `.histogram(\"le\", TimeUnit.MILLISECONDS)` for SDKs that emit\n  bucket bounds in ms (e.g. mini-program\u0027s `miniprogram.request.duration`).\n\n### OAP server\n\n- `Layer.java`: add `WECHAT_MINI_PROGRAM(48, true)` and `ALIPAY_MINI_PROGRAM(49, true)`.\n- `component-libraries.yml`: register `WeChat-MiniProgram: 10002`, `AliPay-MiniProgram: 10003`.\n- `MALCodegenHelper.ENUM_FQCN`: register `TimeUnit → java.util.concurrent.TimeUnit`.\n- Four MAL rule files under `otel-rules/miniprogram/` — per-platform × per-scope:\n  - `wechat-mini-program.yaml` (service + endpoint variants)\n  - `wechat-mini-program-instance.yaml`\n  - `alipay-mini-program.yaml` (subset — Alipay lacks `route` / `script` / `packageLoad` / `first_paint`)\n  - `alipay-mini-program-instance.yaml`\n  - Top-level `filter` gates each file to the correct `miniprogram.platform`.\n  - Each rule explicitly chains `.service(...)` / `.endpoint(...)` — `expSuffix` stays\n    empty in the mixed-scope files so a tail `.service(...)` does not override the\n    endpoint scope (APISIX pattern).\n  - Histogram percentile rule uses `.histogram(\"le\", TimeUnit.MILLISECONDS)` so the\n    bucket-family `le` labels are kept in ms (no default SECONDS × 1000 rescale).\n  - `request_cpm` metric is derived from the histogram\u0027s `_count` family via `.sum()`\n    (DELTA temporality — MAL\u0027s per-minute bucketing sums per-flush counts directly,\n    no `.rate()` / `.increase()` needed).\n- One LAL rule `lal/miniprogram.yaml` using `layer: auto` — one file produces both\n  layers via `sourceAttribute(\"miniprogram.platform\")` dispatch. Extractor emits a\n  `miniprogram_error_count` counter sample per error log.\n- Two log-MAL rules for `error_count`:\n  - `log-mal-rules/miniprogram-wechat.yaml` — gated on `miniprogram_platform \u003d\u003d \u0027wechat\u0027`\n  - `log-mal-rules/miniprogram-alipay.yaml` — gated on `miniprogram_platform \u003d\u003d \u0027alipay\u0027`\n  - Split into two files because `Rules.loadRules` uses `Yaml.loadAs` (single-document).\n- `application.yml` defaults updated:\n  - `enabledOtelMetricsRules` appends `miniprogram/*`\n  - `lalFiles` appends `miniprogram`\n  - `malFiles` appends `miniprogram-wechat,miniprogram-alipay`\n  - `test/e2e-v2/cases/storage/expected/config-dump.yml` mirrored.\n\n### UI\n\n- Menu (`ui-initialized-templates/menu.yaml`): two new entries under the existing\n  `Mobile` group — `WeChat Mini Program` and `Alipay Mini Program`. i18n keys\n  `mobile_wechat_mini_program` / `mobile_alipay_mini_program` are pending in a\n  follow-up `apache/skywalking-booster-ui` PR; the keys fall back to the raw YAML\n  `title` until that lands.\n- Eight new dashboard JSONs:\n  - `wechat_mini_program/{root,service,instance,endpoint}.json`\n  - `alipay_mini_program/{root,service,instance,endpoint}.json`\n  - Each root uses `isRoot: true` with a `ServiceList` widget (4 metric columns).\n  - Service dashboard uses a `Tab` with Overview / Instance / Endpoint / Trace / Log\n    sub-tabs. Alipay service dashboard omits the WeChat-only perf widgets.\n\n### Docs\n\n- `docs/en/setup/backend/backend-wechat-mini-program-monitoring.md` and\n  `backend-alipay-mini-program-monitoring.md`: user-facing setup guides covering\n  prerequisites (SDK ≥ v0.4.0), OAP configuration, SDK init snippet, metric catalog,\n  error-log categories, entity model (incl. the `serviceInstance \u003d\u003d serviceVersion`\n  operator recommendation), SDK-version compatibility, and platform-specific\n  limitations.\n- `docs/menu.yml`: two new entries under `Mobile Monitoring`.\n- `docs/en/changes/changes.md`: entries under `#### OAP Server` and `#### Documentation`.\n\n### Tests\n\n- Two new e2e cases under `test/e2e-v2/cases/miniprogram/{wechat,alipay}/` drive the\n  published `ghcr.io/skyapm/mini-program-monitor/sim-{wechat,alipay}:v0.4.1` image in\n  `MODE\u003dtimed DURATION_MS\u003d60000 SCENARIO\u003ddemo` against OAP wired with the new rules.\n  WeChat: 11 verify cases (service under layer, service/instance/endpoint perf gauges,\n  request_cpm, request_duration_percentile, error_count, LAL-persisted logs). Alipay:\n  8 verify cases (subset matching Alipay\u0027s metric surface).\n- Listener unit tests (`RPCAnalysisListenerTest`,\n  `EndpointDepFromCrossThreadAnalysisListenerTest`) are unchanged from master\n  semantics.\n\n### Follow-ups\n\n- `apache/skywalking-booster-ui` PR for `mobile_wechat_mini_program` /\n  `mobile_alipay_mini_program` i18n keys (menu labels fall back until this lands).\n- Submodule bump once booster-ui PR lands."
    },
    {
      "commit": "681a5be8cb1d4bb77eb40a0d079391ade439a64c",
      "tree": "c1184aeb47d2808cf49a371eadd601d66e451422",
      "parents": [
        "0dabc7033749c0ad9431915d8327e076bc5f4840"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Apr 20 19:03:16 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 19:03:16 2026 +0800"
      },
      "message": "SWIP-12 design + UITemplateInitializer auto-discovery \u0026 dev hot-reload (#13834)\n\n**1. SWIP-12 design doc** (`docs/en/swip/SWIP-12.md`)\nProposes WeChat \u0026 Alipay Mini-Program monitoring as a new pair of `Layer` values. Covers SDK alignment (histogram bucket unit), native-trace SegmentListener SPI, entity model, layer partitioning, dashboards layout, and MAL/OAL scope split. Still a design-only proposal — implementation lands in follow-up PRs.\n\n**2. UITemplateInitializer extensibility + dev hot-reload**\n- `UI_TEMPLATE_FOLDER` is now computed from `Layer.values() + \"custom\"` at class-init time. Adding a new `Layer` enum value is enough — drop a `ui-initialized-templates/\u003clayer-name-lowercased\u003e/` folder on disk and it\u0027s scanned on the next boot. Removes the prior hardcoded allowlist that was easy to miss.\n- `SW_UI_TEMPLATE_FORCE_RELOAD` env var switches the initializer from `addIfNotExist` to a new `addOrReplace` helper on `UITemplateManagementService`. When true, shipped templates overwrite any seeded copy every boot — so dev/extension edits show up after a simple OAP restart without wiping storage. Unset / false preserves the production behavior where operator UI edits persist.\n- `UITemplateCheckerTest` updated to tolerate missing folders (several `Layer` values have no template folders today).\n\n**3. `new-monitoring-feature` skill** (`.claude/skills/new-monitoring-feature/SKILL.md`)\nA wiring map for contributors adding a new layer: which extension point handles which signal (OAL / MAL / LAL / SpanListener / SegmentListener), where contracts live, UI template + submodule touchpoints, and cross-cutting traps."
    },
    {
      "commit": "0dabc7033749c0ad9431915d8327e076bc5f4840",
      "tree": "8e342f02f2f4f6a00f34d42dd3d52f97fddc4ccb",
      "parents": [
        "9e57d91daae1a7511d1e34584fd573af557364c6"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Mon Apr 20 10:22:03 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 09:22:03 2026 +0800"
      },
      "message": "Optimize TraceQueryService.sortSpans from O(N^2) to O(N) (#13831)\n\n`TraceQueryService.sortSpans()` builds the parent-child span tree for trace detail rendering. The existing implementation runs in O(N^2):\n\n- `findRoot()` iterates all N spans and, for each, linearly scans the full list to check whether its parent exists.\n- `findChildren()` is called per root and recursively scans the full list at each node to find direct children.\n\nFor traces with deep call chains or many parallel spans this becomes noticeable latency on every trace detail query (a UI hot path).\n\nThis PR pre-indexes the span list once per trace and turns the algorithm into O(N):\n\n- `Set\u003cString\u003e segmentSpanIds` — membership check used by `findRoot` to decide if a span has a parent in the current list.\n- `Map\u003cString, List\u003cSpan\u003e\u003e childrenByParentSegmentSpanId` — direct lookup for `findChildren`.\n\nThe three helpers (`sortSpans`, `findRoot`, `findChildren`) are now package-private `static` because they have always been pure functions — no instance state. This also makes them directly unit-testable.\n\n## Behavior\n\nPreserved from the previous implementation:\n- Root spans sorted by `startTime`.\n- Children traversed in input order (DFS).\n- Orphaned spans (parent not in the list due to lost/sampled segments) treated as roots.\n- Cross-segment ref parents resolved via the shared `segmentSpanId` namespace (`segmentId + \u0027S\u0027 + spanId`)."
    },
    {
      "commit": "9e57d91daae1a7511d1e34584fd573af557364c6",
      "tree": "924545d839c7674d5096db4f72adb631343738be",
      "parents": [
        "272ba7d04251d780f0ce40f5415e0c12b084b1b4"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sun Apr 19 10:17:02 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Apr 19 10:17:02 2026 +0800"
      },
      "message": "Add iOS/iPadOS app monitoring via OpenTelemetry Swift SDK (SWIP-11) (#13828)\n\n* Add iOS/iPadOS app monitoring via OpenTelemetry Swift SDK (SWIP-11). Includes the `IOS` layer, `IOSHTTPSpanListener` for outbound HTTP client metrics (supports OTel Swift `.old`/`.stable`/`.httpDup` semantic-convention modes via stable-then-legacy attribute fallback), `IOSMetricKitSpanListener` for daily MetricKit metrics (exit counts split by foreground/background, app-launch / hang-time percentile histograms with finite 30 s overflow ceiling), LAL rules for crash/hang diagnostics, Mobile menu, and iOS dashboards.\n* Fix LAL `layer: auto` mode dropping logs after extractor set the layer. Codegen now propagates `layer \"...\"` assignments to `LogMetadata.layer` so `FilterSpec.doSink()` sees the script-decided layer.\n* Fix MetricKit histogram percentile metrics being reported at 1000× their true value — the listener now marks its `SampleFamily` with `defaultHistogramBucketUnit(MILLISECONDS)` so MAL\u0027s default SECONDS→MS rescale of `le` labels is not applied."
    },
    {
      "commit": "272ba7d04251d780f0ce40f5415e0c12b084b1b4",
      "tree": "a96c50c710f3476c59478802c23e17bc95e735d4",
      "parents": [
        "097fe8b448d57bf47fbd428dbf54b71bf08ca1db"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Fri Apr 17 12:17:44 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 17 12:17:44 2026 +0800"
      },
      "message": "Fix: TTL query add metadata TTL and PersistentWorker used wrong TTL for metrics cache if the storage is BanyanDB. (#13827)"
    },
    {
      "commit": "097fe8b448d57bf47fbd428dbf54b71bf08ca1db",
      "tree": "0e7f92af1a866b8f972147174a5a512744c00f5b",
      "parents": [
        "52af116cb49e5f9a91e5a65ee6b632f23b11deea"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 16 18:44:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 16 18:44:33 2026 +0800"
      },
      "message": "Add OTLP/HTTP receiver support for traces, logs, and metrics (#13826)\n\nEach existing OTLP gRPC handler now also registers an HTTP handler\nSupported endpoints:\n- POST /v1/traces (protobuf + JSON)\n- POST /v1/logs (protobuf + JSON)\n- POST /v1/metrics (protobuf + JSON)"
    },
    {
      "commit": "52af116cb49e5f9a91e5a65ee6b632f23b11deea",
      "tree": "81b7981c74b76f151bc91f8ae0df35b3ae551a14",
      "parents": [
        "9fda21938fcbf71d77a057105c89d8c1e1d33f5e"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 16 11:39:52 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 16 11:39:52 2026 +0800"
      },
      "message": "Add SpanListener SPI, LAL sourceAttribute() and layer:auto mode (#13825)\n\n### SWIP-11 general-purpose enhancements for extensible trace and log processing\n\nThree features from [SWIP-11](https://github.com/apache/skywalking/discussions/13821):\n\n1. **LAL `sourceAttribute()` function** — non-persistent source context (e.g., OTLP resource attributes) accessible to LAL scripts. Values are NOT stored in `tagsRawData`; scripts selectively copy useful ones into persistent tags.\n\n2. **LAL `layer: auto` mode** — rules with `layer: auto` match logs where `service.layer` is absent. The script determines the layer in the extractor. `abort` \u003d \"not mine, fall back to GENERAL\"; `drop` \u003d \"mine but sampled, no fallback\". If all auto rules abort, falls back to GENERAL automatically.\n\n3. **Two-phase `SpanListener` SPI** — extensible trace span processing in `server-core` (`core.trace` package). Phase 1 (`onOTLPSpan`) runs before Zipkin conversion (OTLP-only). Phase 2 (`onZipkinSpan`) runs after conversion (all trace sources). `requiredModules()` for dependency checking. Lazy init on first span arrival. GenAI refactored from hardcoded `SpanForward.processGenAILogic()` to `GenAISpanListener.onZipkinSpan()`. Removes zipkin-receiver-plugin dependency on gen-ai-analyzer.\n\nAdditional fixes:\n- Register `SpanListenerManager` in `MockCoreModuleProvider` (profile exporter tool)\n- Fix GenAI e2e docker-compose: `provider-py` now depends on `provider` healthcheck to avoid startup race"
    },
    {
      "commit": "9fda21938fcbf71d77a057105c89d8c1e1d33f5e",
      "tree": "a89b6f343338b737e537ab820cc0a9ece8e9ef4f",
      "parents": [
        "9e3dcf1216afcd174e0344725566d5474ce9e0c4"
      ],
      "author": {
        "name": "Mukunda Rao Katta",
        "email": "mukunda.vjcs6@gmail.com",
        "time": "Wed Apr 15 05:07:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 20:07:03 2026 +0800"
      },
      "message": "Fix \u0027seperated\u0027 typo in apm-webapp application.yml comment (#13824)"
    },
    {
      "commit": "9e3dcf1216afcd174e0344725566d5474ce9e0c4",
      "tree": "dc8ad748492e2338723b2d050624a6d39aa3e9f2",
      "parents": [
        "35a8b1cac9cf2e684faf0de0dfdaf99119b4b4ea"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Wed Apr 15 20:17:13 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 19:17:13 2026 +0800"
      },
      "message": "Add unit tests for Elasticsearch profiling query DAOs (#13820)"
    },
    {
      "commit": "35a8b1cac9cf2e684faf0de0dfdaf99119b4b4ea",
      "tree": "3d1c2135834b82e764cdbccef70924d5a7b2f7ef",
      "parents": [
        "9e55f574bd11117fb9afe4c17f0f8acd0577424d"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 15 15:42:52 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 15:42:52 2026 +0800"
      },
      "message": "Add SWIP-11: Support iOS App Monitoring via OpenTelemetry (#13822)"
    },
    {
      "commit": "9e55f574bd11117fb9afe4c17f0f8acd0577424d",
      "tree": "0de5f055582c230e218fec3764889174502996e3",
      "parents": [
        "9ea3bab32f8344ece2949f640a2baf52d7c6d465"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 15 13:47:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 13:47:34 2026 +0800"
      },
      "message": "Bump infra-e2e to testcontainers-go v0.42.0 and remove deprecated compose version field (#13817)\n\n**Changes:**\n- Bump `skywalking-infra-e2e` to `530f997` (merged apache/skywalking-infra-e2e#146), which migrates testcontainers-go from v0.11.1 to v0.42.0, using Docker Compose v2 plugin natively and removing docker-compose v1 dependency.\n- Remove deprecated `version` field from all docker-compose files (Compose v2 ignores it with a warning).\n\n**What the infra-e2e change does:**\n- Migrates testcontainers-go v0.11.1 → v0.42.0, using native `compose.NewDockerComposeWith` API\n- Deletes ~686 lines of custom `DockerContainer`/`DockerProvider` reimplementation\n- Removes docker-compose v1 binary installation from CI `action.yaml`\n- Adds TCP port waiting after `docker compose up -d` (matching old behavior)\n- Restores `DOCKER_API_VERSION` negotiation for Docker 29+ compatibility\n- User-facing contract (e2e.yaml format, env vars, log paths, CLI) is preserved"
    },
    {
      "commit": "9ea3bab32f8344ece2949f640a2baf52d7c6d465",
      "tree": "f49657fce2e9ea68372a407c757098b657ed7b21",
      "parents": [
        "61a241bbaeaa1558e16af2b9517dbc4ff22e2f6b"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Wed Apr 15 11:48:16 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 11:48:16 2026 +0800"
      },
      "message": "PromQL: support querying Zipkin metadata and TraceQL support more tags and variables querying in Grafana (#13819)"
    },
    {
      "commit": "61a241bbaeaa1558e16af2b9517dbc4ff22e2f6b",
      "tree": "223d5e8c0d5b1c8ca37bf8086cc1ad3ed37ff4b5",
      "parents": [
        "55638d4c9a0df01cd2dec49e76d50d891947beb6"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Wed Apr 15 10:09:41 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 09:09:41 2026 +0800"
      },
      "message": "Fix missing isMergedTable check in ProfileTaskQueryEsDAO.getById() (#13813)"
    },
    {
      "commit": "55638d4c9a0df01cd2dec49e76d50d891947beb6",
      "tree": "60ce82f8dfa7bf7febd629bb3966becd61e7d713",
      "parents": [
        "b4a8811df9f48c66edb61c7b72d0293e3ecfa7cf"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 15 07:11:16 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 07:11:16 2026 +0800"
      },
      "message": "Fix Virtual GenAI e2e test base URL for openai-java SDK compatibility (#13818)\n\nSpring AI 2.0.0-SNAPSHOT switched to the official openai-java SDK which\nexpects the base URL to include /v1. Without it, requests go to\n/llm/chat/completions instead of /llm/v1/chat/completions, causing 404."
    },
    {
      "commit": "b4a8811df9f48c66edb61c7b72d0293e3ecfa7cf",
      "tree": "2e7fd1197a5a323bbd82f1b4668f485f91fe8819",
      "parents": [
        "42c613bea94999a6cc8e805ed4c8c7659f3a735c"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Apr 14 11:16:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 14 11:16:33 2026 +0800"
      },
      "message": "Fix duplicate calls in EndpointTopologyBuilder (#13816)\n\n* Fix duplicate calls in EndpointTopologyBuilder and enforce no-duplicate in e2e\n\nEndpointTopologyBuilder.build() created a new Call for every CallDetail\nwithout deduplication, unlike ServiceTopologyBuilder which uses a HashMap.\nThis caused duplicate call entries when storage returns multiple records\nfor the same endpoint relation.\n\nAlso switch all dependency topology e2e expected files from `contains`\nto `containsOnce` to enforce strict 1:1 matching and catch duplicates.\n\n* Bump infra-e2e to ef073ad and add noDuplicates to dependency expected files\n\nUpgrade skywalking-infra-e2e to include the noDuplicates pipe function.\nAdd | noDuplicates to all dependency topology expected files for both\nnodes and calls, ensuring storage-level duplicates are caught in e2e."
    },
    {
      "commit": "42c613bea94999a6cc8e805ed4c8c7659f3a735c",
      "tree": "32b962cd0b40c656fff75d7a185f80e07e503903",
      "parents": [
        "7abe7b3b85215a254e2aec9d1e6858848267c728"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Apr 13 21:51:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 13 21:51:00 2026 +0800"
      },
      "message": "Use containsOnce for metadata e2e expected files (#13815)\n\nMigrate outer-level contains to containsOnce in service, instance,\nendpoint, and topology expected files (101 files). containsOnce uses\nbacktracking for optimal 1:1 assignment between expected and actual\nentries, which is more precise for metadata where we know exactly\nwhich entries should exist.\n\nInner nested contains (layers, tags, etc.) are left unchanged."
    },
    {
      "commit": "7abe7b3b85215a254e2aec9d1e6858848267c728",
      "tree": "703098d69378bb6a15cc82ca4ddf2b70492f227d",
      "parents": [
        "5e9b9e2c707b0866f878e7df22e1b006ae5ca725"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Apr 13 16:10:48 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 13 16:10:48 2026 +0800"
      },
      "message": "Update infra-e2e to b2a6639 and fix duplicate expected entries (#13812)\n\nBump `skywalking-infra-e2e` from `e26033e` to `b2a6639` (apache/skywalking-infra-e2e#143), which enforces distinct matching in `contains` — each actual item can only satisfy one expected entry.\n\nRemove duplicate expected entries that only passed due to the old buggy behavior where a single actual item could satisfy multiple expected entries:\n- `alarm/expected/silence-after-webhook.yml`: reduce from 11 to 5 entries (removed duplicate `service_percentile_rule` and `comp_rule` entries)\n- `rover/process/istio/expected/service.yml`: remove duplicate `ratings.default` entry"
    },
    {
      "commit": "5e9b9e2c707b0866f878e7df22e1b006ae5ca725",
      "tree": "ce3544c1904aa1489ea01bf27c9c69f5fe0e2b8f",
      "parents": [
        "7e93babb2f694e6ed47cd09871ec50f85134a0a2"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Fri Apr 10 21:08:24 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 10 20:08:24 2026 +0800"
      },
      "message": "Add unit tests for 8 more JDBC query DAOs (inline SQL) (#13809)"
    },
    {
      "commit": "7e93babb2f694e6ed47cd09871ec50f85134a0a2",
      "tree": "087de5efad07e135c2ca167095971e7d4dedd568",
      "parents": [
        "073238cd2ad2eaa1ecf948bbc2b04df3fe81e927"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Apr 10 18:47:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 10 18:47:59 2026 +0800"
      },
      "message": "Fix Virtual GenAI e2e test failure (#13810)\n\nPin Python dependencies in the OpenAI e2e test Dockerfile and remove\n`opentelemetry-bootstrap --action\u003dinstall` which auto-installs the official `opentelemetry-instrumentation-openai-v2` package that is incompatible with OTel SDK 1.41.0, crashing GenAI instrumentation\nat runtime."
    },
    {
      "commit": "073238cd2ad2eaa1ecf948bbc2b04df3fe81e927",
      "tree": "2ba1d5cbcef1d69aaec13052bd67c25f78eaaec1",
      "parents": [
        "2f03a0179157a3b06416221516733825bb452521"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Thu Apr 09 21:40:35 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 09 20:40:35 2026 +0800"
      },
      "message": "Add unit tests for 6 more JDBC query DAOs (#13808)"
    },
    {
      "commit": "2f03a0179157a3b06416221516733825bb452521",
      "tree": "f3a822f10bcac36e476764ad788a0f8472722856",
      "parents": [
        "cb742a24a22474a0ad734475ceaa3e036ac556a5"
      ],
      "author": {
        "name": "mrproliu",
        "email": "741550557@qq.com",
        "time": "Thu Apr 09 11:54:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 09 11:54:56 2026 +0800"
      },
      "message": "Fix missing `taskId` filter in pprof task log query and its DB implementations (#13807)"
    },
    {
      "commit": "cb742a24a22474a0ad734475ceaa3e036ac556a5",
      "tree": "53fc9a48bfe6b0796d7f00ea1489accf8e8c61e4",
      "parents": [
        "ad12d91e8ef55d22e086a4e9364e7173956f0101"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Wed Apr 08 19:51:34 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 08 18:51:34 2026 +0800"
      },
      "message": "Fix wrong merged table check in JFRDataQueryEsDAO (#13805)"
    },
    {
      "commit": "ad12d91e8ef55d22e086a4e9364e7173956f0101",
      "tree": "185866fbbf2a55736ea0d87f0c235c5d24acc002",
      "parents": [
        "4742a3062c6ec5fa9ab77f72dfa8280a1fef4ee4"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Wed Apr 08 17:10:38 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 08 16:10:38 2026 +0800"
      },
      "message": "Add unit tests for JDBCEventQueryDAO, JDBCBrowserLogQueryDAO, and JDBCProfileThreadSnapshotQueryDAO (#13802)"
    },
    {
      "commit": "4742a3062c6ec5fa9ab77f72dfa8280a1fef4ee4",
      "tree": "1fb6186593058a073c1127bcca60137fa5e58a20",
      "parents": [
        "acb63379cdaa80837a2c02835be837b12afb06a8"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Wed Apr 08 15:08:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 08 15:08:46 2026 +0800"
      },
      "message": "polish TraceQL service docs (#13804)"
    },
    {
      "commit": "acb63379cdaa80837a2c02835be837b12afb06a8",
      "tree": "16ea6bc5c3a2b8430d37b240a9b104bdc9e44872",
      "parents": [
        "ebda65ca12b64ebfb2eb8e10a6348288f9b5c4c3"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Tue Apr 07 17:38:11 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 07 17:38:11 2026 +0800"
      },
      "message": "Add TraceQL configuration in docs ui-grafana.md (#13803)"
    },
    {
      "commit": "ebda65ca12b64ebfb2eb8e10a6348288f9b5c4c3",
      "tree": "250101903cb2fe8e8d3d38bf47e03cda4dfe7fe0",
      "parents": [
        "33ad987a72dced25521fe4b2c62fba3d4e187fc9"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Apr 07 09:52:21 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 07 09:52:21 2026 +0800"
      },
      "message": "Add weighted handler support to BatchQueue adaptive partitioning (#13801)\n\n`PartitionPolicy.resolve()` now accepts a weighted handler count instead of raw handler count.\n`BatchQueue.addHandler(type, handler, weight)` overload allows callers to specify partition weight per handler type.\n\n**L1 (MetricsAggregateWorker):** MAL metrics use weight 0.05 (vs 1.0 for OAL). Rationale: MAL emits ~500 items/type per scrape interval. With 20,000-slot buffers, ~40 MAL types can safely share one partition (20,000 / 500 \u003d 40). Weight 0.05 ≈ 1/20 gives 2x headroom.\n\n**L2 (MetricsPersistentMinWorker):** No weight differentiation. After L1 pre-aggregation, both OAL and MAL have similar per-minute burst patterns.\n\n**Impact (8-core, 642 OAL + 1,247 MAL types):**\n\n| | L1 Before | L1 After | Reduction |\n|---|---|---|---|\n| Partitions | 1,045 | 452 | 57% |\n| Array overhead | 167 MB | 72 MB | 57% |"
    },
    {
      "commit": "33ad987a72dced25521fe4b2c62fba3d4e187fc9",
      "tree": "eb8907815b9f0bb178157ca1c129dee9d53b9a7f",
      "parents": [
        "8907f0ce7aa1e211fc4df1c31ed962ae37b76a99"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Apr 06 23:05:26 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 23:05:26 2026 +0800"
      },
      "message": "Support MCP observability for Envoy AI Gateway (#13791)\n\n**MAL rules** (new files):\n- `gateway-mcp-service.yaml` — 13 MCP service-level metrics (request CPM/latency/percentile, method CPM, error CPM, initialization latency, capabilities, per-backend breakdown)\n- `gateway-mcp-instance.yaml` — 13 MCP instance-level metrics\n\n**LAL rules** (modified `envoy-ai-gateway.yaml`):\n- Split into two rules: `envoy-ai-gateway-llm-access-log` and `envoy-ai-gateway-mcp-access-log`\n- LLM logs: persist error responses (\u003e\u003d 400) and upstream failures only\n- MCP logs: persist error responses (\u003e\u003d 400) only\n- Both rules tag `ai_route_type` (`llm` or `mcp`) for searchable filtering\n\n**Dashboard** (modified service + instance JSON):\n- Added **MCP** tab with 9 widgets (service) / 6 widgets (instance): request CPM, latency avg/percentile, error CPM, method CPM, initialization latency, backend breakdown\n\n**E2E test** (modified):\n- Added `mcp-server` service (`tzolov/mcp-everything-server:v3` — MCP reference server with StreamableHttp)\n- Added MCP request steps (initialize + tools/list + tools/call)\n- Added MCP metric verification cases\n- Log query uses `ai_route_type\u003dllm` tag filter\n\n**Config**:\n- Added `ai_route_type` to `searchableLogsTags` in `application.yml`\n- Fixed aigw healthcheck binary path (`/app` instead of `aigw`)"
    },
    {
      "commit": "8907f0ce7aa1e211fc4df1c31ed962ae37b76a99",
      "tree": "f170f609174ef8e495f7e625f17c20547aa0d032",
      "parents": [
        "86b6d62d66e8d33bafeac94a3fb21ef390729863"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Mon Apr 06 23:00:11 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 22:00:11 2026 +0800"
      },
      "message": "Add unit tests for JDBC query DAO SQL building (#13800)\n\nAdd unit tests for SQL building in JDBCAlarmQueryDAO, JDBCLogQueryDAO, JDBCTraceQueryDAO, and JDBCTopologyQueryDAO, verifying correct WHERE clause construction, JOIN generation, parameter binding, and ORDER BY/LIMIT handling across various filter combinations. "
    },
    {
      "commit": "86b6d62d66e8d33bafeac94a3fb21ef390729863",
      "tree": "afaa28b96e0ae3f5203565f77f4cef6ede0c7e9b",
      "parents": [
        "1b66915a579275c086bc37ca9faa1c67bb77b1aa"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Mon Apr 06 21:46:00 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 20:46:00 2026 +0800"
      },
      "message": "Fix duplicate TABLE_COLUMN condition in JDBCMetadataQueryDAO.findEndpoint() (#13794)\n\n`findEndpoint()` was adding the `TABLE_COLUMN \u003d ?` WHERE condition twice and binding `EndpointTraffic.INDEX_NAME` as a parameter twice, due to a copy-paste error.\n\nThe generated SQL was:\n\n```sql\nselect * from endpoint_traffic where table_name \u003d ? and service_id \u003d ? and table_name \u003d ? ...\n```\n\nThe second `table_name \u003d ?` condition is redundant. All other methods in `JDBCMetadataQueryDAO` add the `TABLE_COLUMN` condition exactly once. The fix removes the duplicate condition and its corresponding parameter binding."
    },
    {
      "commit": "1b66915a579275c086bc37ca9faa1c67bb77b1aa",
      "tree": "fc726f854e8ac0164e1dfae1ac1acfd4d9fa6494",
      "parents": [
        "3bb501fd5f1b74c36c2258a1cf798df7db5ebce4"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Sat Apr 04 12:03:31 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Apr 04 11:03:31 2026 +0800"
      },
      "message": "Fix missing AND keyword in JDBCEBPFProfilingTaskDAO.getTaskRecord() SQL (#13789)"
    },
    {
      "commit": "3bb501fd5f1b74c36c2258a1cf798df7db5ebce4",
      "tree": "3f5d3cfed9fe3bf8587981f8ed72a96ea1cc7c60",
      "parents": [
        "f213c3b61022c9b00c61541ed227433406724291"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Sat Apr 04 10:36:58 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Apr 04 09:36:58 2026 +0800"
      },
      "message": "Fix missing parentheses around OR conditions in JDBCZipkinQueryDAO.getTraces() (#13790)\n\n Fix incorrect trace ID filter (OR chain without parentheses) in JDBCZipkinQueryDAO.getTraces(), replaced with IN clause. "
    },
    {
      "commit": "f213c3b61022c9b00c61541ed227433406724291",
      "tree": "cbbdda3bcf72965c8d353bb836048d615daed062",
      "parents": [
        "2f652be84ff6131fc4c9477a405ff2cb1d1066e8"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Fri Apr 03 14:56:28 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 03 13:56:28 2026 +0800"
      },
      "message": "Push taskId filter down to storage layer in AsyncProfilerTaskLog query (#13787)"
    },
    {
      "commit": "2f652be84ff6131fc4c9477a405ff2cb1d1066e8",
      "tree": "e71fa804702c8dc4959402b117e67126b2b7ceb1",
      "parents": [
        "9aac8301ef9aa0cc6421cacd1851f2d45737c4cc"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Fri Apr 03 11:55:36 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 03 10:55:36 2026 +0800"
      },
      "message": "Remove GroupBy.field_name from BanyanDB MeasureQuery request building (#13786)"
    },
    {
      "commit": "9aac8301ef9aa0cc6421cacd1851f2d45737c4cc",
      "tree": "1f51c5d7618cde7f3df06b9e04514ed420023e77",
      "parents": [
        "ea1c23cb8c772fd4d765765630cd521d4bbddbb4"
      ],
      "author": {
        "name": "Hyunjin-Jeong",
        "email": "hyun0524e@naver.com",
        "time": "Fri Apr 03 00:26:45 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 02 23:26:45 2026 +0800"
      },
      "message": "Fix missing taskId filter and incorrect IN clause in JDBC profiling query DAOs (#13785)\n\nIn JDBCJFRDataQueryDAO and JDBCPprofDataQueryDAO, the taskId parameter was\nvalidated but never added to the SQL WHERE clause, causing all profiling data\nto be returned regardless of the task. Additionally, the IN clause for\ninstanceIds used a single comma-joined string parameter instead of individual\nbind parameters, which always returned empty results when multiple instances\nwere specified."
    },
    {
      "commit": "ea1c23cb8c772fd4d765765630cd521d4bbddbb4",
      "tree": "b260854f7df170dc3db3632a211fc54b7c130643",
      "parents": [
        "8544af1e3a2cba50eb6373c25e82c45789e98888"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 02 17:22:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 02 17:22:56 2026 +0800"
      },
      "message": "Add Zipkin Virtual GenAI e2e test with zipkin_json exporter (#13784)"
    },
    {
      "commit": "8544af1e3a2cba50eb6373c25e82c45789e98888",
      "tree": "7da2815984cb37f03b183cf1b56549e1a8dde513",
      "parents": [
        "b20e6ab31a141ad702bfe8ab0eee58bd7c184f42"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 02 15:11:19 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 02 15:11:19 2026 +0800"
      },
      "message": "Remove dead code and unused imports from DSL script execution tests (#13783)"
    },
    {
      "commit": "b20e6ab31a141ad702bfe8ab0eee58bd7c184f42",
      "tree": "1f0b842f8f1a101a53b0234d7f842a8f5fcea63b",
      "parents": [
        "92216447cf26931a113b024e2ce3ec0a336b82f7"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Apr 02 11:59:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 02 11:59:45 2026 +0800"
      },
      "message": "Remove Groovy v1-v2 checker, replace with v2-only DSL script execution tests (#13782)\n\n- Delete `test/script-cases/` entirely — Groovy v1 modules and v1-v2 checker removed from build\n- Move hierarchy tests + scripts into `oap-server/analyzer/hierarchy/`\n- Move LAL tests + scripts into `oap-server/analyzer/log-analyzer/`\n- Create `oap-server/analyzer/meter-analyzer-scripts-test/` for MAL tests (separate module for `ProcessRegistry` FQCN isolation)\n- MAL `.data.yaml` files reference starter configs via `script:` field — no duplicated production scripts\n- `MalRuleLoader.loadFromDataFiles()` — data-driven loader that scans `.data.yaml` first, resolves scripts from `script:` path\n- Fix checkstyle JMH exclusion patterns (`jmh_generated` not `generated`)"
    },
    {
      "commit": "92216447cf26931a113b024e2ce3ec0a336b82f7",
      "tree": "d5746c612bdddddd237df1682348ffdc879f8578",
      "parents": [
        "f79451d3de23edb15277f5b6e315dc6779e73e1f"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 01 14:19:14 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 01 14:19:14 2026 +0800"
      },
      "message": "10.4.0 release and begin 10.5.0 iteration (#13778)"
    },
    {
      "commit": "f79451d3de23edb15277f5b6e315dc6779e73e1f",
      "tree": "877b7910bf35cf61b570764b9489310b13bb2649",
      "parents": [
        "2da8c5b1c7a29f98575aa1e5a01d610f4a901296"
      ],
      "author": {
        "name": "peachisai",
        "email": "2581009893@qq.com",
        "time": "Wed Apr 01 10:53:42 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 01 10:53:42 2026 +0800"
      },
      "message": "Support virtual gen ai analysis for otlp and zipkin traces (#13767)"
    },
    {
      "commit": "2da8c5b1c7a29f98575aa1e5a01d610f4a901296",
      "tree": "19ded9ef4cc7f3ce301cf55ae51a7b233c7acde8",
      "parents": [
        "ef4a8c61dbdc191845d9cf8a20e06747b56374ae"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 01 08:12:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 01 08:12:24 2026 +0800"
      },
      "message": "Update skywalking-ui submodule to include Envoy AI Gateway i18n menu entries (#13774)"
    },
    {
      "commit": "ef4a8c61dbdc191845d9cf8a20e06747b56374ae",
      "tree": "51fce934eba80355829787b301c1f57dfc15412d",
      "parents": [
        "d0cc16ac14aadea830513dc0df69f42f850bf56f"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Apr 01 07:17:12 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 01 07:17:12 2026 +0800"
      },
      "message": "Support Envoy AI Gateway observability (SWIP-10) (#13772)\n\nAdd Envoy AI Gateway as a new monitored layer (`ENVOY_AI_GATEWAY`) in SkyWalking, receiving\nGenAI metrics and access logs via OTLP push from the AI Gateway.\n\n**Changes:**\n- New layer `ENVOY_AI_GATEWAY(46, true)` in `Layer.java`\n- MAL rules: 2 rule files (service + instance) with 38 metrics total — aggregates, per-provider\n  and per-model breakdowns including token usage, latency, TTFT, TPOT\n- LAL rules: access log sampling (errors, upstream failures, high token cost)\n- UI dashboards: root (with doc link), service (Overview/Providers/Models/Log/Instances tabs),\n  instance (Overview/Providers/Models/Log tabs)\n- OTel receiver fixes:\n  - Convert data point attribute dots to underscores (consistent with resource attributes)\n  - Change `LABEL_MAPPINGS` to fallback-only — `service.name` preserved as `service_name` tag\n  - Remove unused `service.name → job_name` mapping (MAL checker: 1268/1268 rules pass)\n  - OTLP log handler: prefer `service.instance.id` (OTel spec) with fallback to `service.instance`\n- `UITemplateInitializer`: register `ENVOY_AI_GATEWAY` template folder\n- `SampleFamily`: add `toString()` and `debugDump()` for MAL debugging\n- Documentation: setup guide, OTel receiver label conversion, LAL OTLP mapping, marketplace GenAI\n- E2e test: docker-compose with `ai-gateway-cli` + Ollama (qwen2.5:0.5b)"
    },
    {
      "commit": "d0cc16ac14aadea830513dc0df69f42f850bf56f",
      "tree": "6f7d9ad2e6b6924103ea3a22262791918a9d117f",
      "parents": [
        "c6a07a24c6adabaae99dad675a2b08676681c5a2"
      ],
      "author": {
        "name": "peachisai",
        "email": "2581009893@qq.com",
        "time": "Tue Mar 31 20:56:20 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 31 20:56:20 2026 +0800"
      },
      "message": "Fix zipkin receiver compatibility with application/x protobuf content type (#13773)"
    },
    {
      "commit": "c6a07a24c6adabaae99dad675a2b08676681c5a2",
      "tree": "f00c35bd4096511791eae153cbf2a68930d11f21",
      "parents": [
        "07543c4cec5f8da6f73f0ba664f1c298249e9e94"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Tue Mar 31 09:10:51 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 31 09:10:51 2026 +0800"
      },
      "message": "Support TraceQL and Tempo API for SkyWalking native trace query. (#13770)"
    },
    {
      "commit": "07543c4cec5f8da6f73f0ba664f1c298249e9e94",
      "tree": "2eb410aa574cbe3abc27e33b856bdeea426cf770",
      "parents": [
        "c87e4df98da0458f8f4c6cba58cb41f9eb2ec972"
      ],
      "author": {
        "name": "Fine0830",
        "email": "fanxue0830@gmail.com",
        "time": "Mon Mar 30 13:11:06 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 30 13:11:06 2026 +0800"
      },
      "message": "sync ui (#13769)"
    },
    {
      "commit": "c87e4df98da0458f8f4c6cba58cb41f9eb2ec972",
      "tree": "2e5a53a4f3f4161c6f3c87de7515932d2480c379",
      "parents": [
        "6248d1b7259dd24cc8218a8513d14cf018c519f0"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Mar 30 09:26:01 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 30 09:26:01 2026 +0800"
      },
      "message": "Add BanyanDB cluster mode e2e tests for Istio (ALS + metrics) (#13768)\n\nAdds BanyanDB cluster mode variants for Istio ALS and Envoy Metrics Service e2e tests.\n\n**BanyanDB cluster configuration:**\n- 2 liaison nodes + 2 data nodes (hot only)\n- DNS service discovery (no etcd)\n- Native metadata storage (property mode)\n- Access log flags commented out (available for debugging, not enabled by default)\n\n**New files:**\n- `test/e2e-v2/cases/istio/als/banyandb/e2e.yaml` — ALS with BanyanDB cluster\n- `test/e2e-v2/cases/istio/metrics/banyandb/e2e.yaml` — Envoy metrics service with BanyanDB cluster\n- `test/e2e-v2/cases/istio/als/als-cases.yaml` — shared verify cases (extracted from als/e2e.yaml)\n- `test/e2e-v2/cases/istio/metrics/metrics-cases.yaml` — shared verify cases (extracted from metrics/e2e.yaml)\n\n**Workflow improvements:**\n- `e2e-test-istio` job: added `storage` matrix dimension (`ES`/`BanyanDB`) for ALS tests\n- `e2e-test` job: added `Istio Metrics Service BanyanDB 1.29.0` to matrix\n- **Default docker images switched from JDK 11 to JDK 25** for all e2e tests\n  (JDK 11/17 compatibility covered by `e2e-test-java-versions` only)\n- **Clean job names**: removed e2e YAML paths from display names\n  - `e2e-test`: uses `${{ matrix.test.name }}` directly (e.g., `Cluster ZK ES`, `Agent PHP`)\n  - `e2e-test-istio`: `Istio ALS (k8s-mesh, ES, 1.29.0, 28)`\n  - `e2e-test-istio-ambient`: `Istio Ambient ALS (k8s-mesh, 1.29.0, 28)`\n  - `e2e-test-java-versions`: `Java 11`, `Java 17`, `Java 25`\n  - `e2e-test-banyandb-stages`: `BanyanDB Stages`\n- Access log flags commented out in all BanyanDB configs (Compose + Kind)\n  with instructions for enabling during debugging\n\n**Data collection on failure:**\n\nCollects from all BanyanDB pods via `label-selector: app.kubernetes.io/name\u003dbanyandb`:\n\n| Pod | Collected |\n|---|---|\n| `skywalking-banyandb-liaison-{0,1}` | `/tmp/accesslog/` (when access logs enabled) |\n| `skywalking-banyandb-data-hot-{0,1}` | `/tmp/{trace,stream,measure,property,schema-property}/` |"
    },
    {
      "commit": "6248d1b7259dd24cc8218a8513d14cf018c519f0",
      "tree": "44e05c4ba7d03fa7c051217a982d5447b482100d",
      "parents": [
        "5be7e79a9b2cea4e8ccafa5467522eb85bbbb1e1"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sun Mar 29 14:40:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Mar 29 14:40:37 2026 +0800"
      },
      "message": "Collect BanyanDB data and access logs on e2e failure, bump BanyanDB, update CLAUDE.md (#13765)\n\n**Problem:** When BanyanDB-related e2e tests fail, only container logs are uploaded. The actual\ndata files and access logs are lost with container cleanup.\n\n**Changes:**\n\n**1. Collect BanyanDB data and access logs on failure**\n- Update `skywalking-infra-e2e` to `e26033e` (collect-on-failure with glob pattern support)\n- Add `cleanup.collect` to all 18 BanyanDB e2e YAMLs\n- Collected directories: `/tmp/trace/`, `/tmp/stream/`, `/tmp/measure/`, `/tmp/property/`, `/tmp/schema-property/`, `/tmp/accesslog/`\n\n**2. Enable BanyanDB ingestion and query access logs**\n- Compose: `--enable-ingestion-access-log --enable-query-access-log --access-log-root-path /tmp`\n- Kind: `BYDB_ENABLE_INGESTION_ACCESS_LOG`, `BYDB_ENABLE_QUERY_ACCESS_LOG`, `BYDB_ACCESS_LOG_ROOT_PATH` via Helm `--set` with escaped quotes (`\\\"true\\\"`) to force string type\n- BanyanDB auto-creates `/tmp/accesslog/` subdirectory\n- 8 access log files per node: `{measure,stream,trace,property}-{ingest,query}-\u003ctimestamp\u003e`\n\n**3. Remove custom root-path overrides**\n- Removed `--stream-root-path /tmp/stream-data --measure-root-path /tmp/measure-data` from base compose\n- All modules use default `/tmp` root path consistently\n- Updated stages data-generate workflow and volume mounts\n\n**4. Bump BanyanDB to `d0f41f9a`**\n- Adds auto-creation of accesslog directory (no mkdir workaround needed)\n\n**5. Update CLAUDE.md**\n- Added \"Analysis and Design Principles\" section: never guess, read source code, verify locally, ask developer\n- Docker image registry guidance (ghcr.io vs Docker Hub)\n\n**Verified results:**\n\nDocker Compose (`test-logs-BanyanDB`):\n```\nbanyandb-data/banyandb/tmp/\n  trace:           2.8M   (818 files) ✅\n  stream:          404K   (113 files) ✅\n  measure:         476K   (109 files) ✅\n  property:        1.1M   (6 files)   ✅\n  schema-property:  14M   (8 files)   ✅\n  accesslog:       2.8M   (8 files)   ✅\n```\n\nKind / K8s (`test-logs-eBPF Profiling On CPU BanyanDB`):\n```\nbanyandb-data/istio-system/skywalking-banyandb-0/tmp/\n  trace:           0B    (2 files)    ✅\n  stream:        252K    (75 files)   ✅\n  measure:        20K    (8 files)    ✅\n  property:      1.1M    (8 files)    ✅\n  schema-property: 14M   (8 files)    ✅\n  accesslog:     1.1M    (8 files)    ✅\n```"
    },
    {
      "commit": "5be7e79a9b2cea4e8ccafa5467522eb85bbbb1e1",
      "tree": "4fa60b55f3d0bf339696571ccabdbc8a18806253",
      "parents": [
        "0e46059cfea0221950825a94cc275fdfe04e49c2"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 27 08:05:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 27 08:05:33 2026 +0800"
      },
      "message": "Refactor MAL codegen into focused classes, fix CI issues (#13763)\n\n**1. Refactor MALClassGenerator (1484 → 5 focused classes)**\n\nSplit by responsibility with clear comments showing which MAL expressions each class handles:\n\n| Class | Lines | Responsibility |\n|---|---|---|\n| MALClassGenerator | ~350 | Public API + orchestration |\n| MALExprCodegen | ~310 | Expression → Java source (variable-per-expression model) |\n| MALMethodChainCodegen | ~340 | Method chain + extension + argument codegen |\n| MALMetadataExtractor | ~370 | Static AST → metadata extraction |\n| MALBytecodeHelper | ~290 | Javassist class naming, debug, bytecode attrs |\n\n**2. Variable-per-expression codegen (replaces shared `sf` variable)**\n\nEach metric expression gets its own named variable (e.g. `_metric1`, `_metric2`). Chain calls reassign to the same variable. Binary operations combine variables directly. No save/restore dance. Extension `::` calls work everywhere including sub-expressions.\n\nBefore:\n```java\nSampleFamily sf;\nsf \u003d ((SF) samples.getOrDefault(\"metric1\", EMPTY));\nsf \u003d sf.sum(...);\nSampleFamily _t0 \u003d sf;       // save\nsf \u003d ((SF) samples.getOrDefault(\"metric2\", EMPTY));\nsf \u003d sf.avg(...);\nsf \u003d _t0.plus(sf);            // restore + combine\nreturn sf;\n```\n\nAfter:\n```java\nSampleFamily _metric1 \u003d ((SF) samples.getOrDefault(\"metric1\", EMPTY));\n_metric1 \u003d _metric1.sum(...);\nSampleFamily _metric2 \u003d ((SF) samples.getOrDefault(\"metric2\", EMPTY));\n_metric2 \u003d _metric2.avg(...);\n_metric1 \u003d _metric1.plus(_metric2);\nreturn _metric1;\n```\n\n**3. Unified codegen path**\n\nRemoved the inline expression codegen path. Complex expression arguments are pre-computed to temp variables via the statement-based path. Single codegen strategy eliminates duplication.\n\n**4. Developer extension docs**\n\nNew docs under Contributing Guides \u003e Project Extensions:\n- `docs/en/guides/mal-extension.md` — MAL `namespace::method()` SPI for developers\n- `docs/en/guides/lal-extension.md` — LAL custom input/output types for developers\n- Renamed `source-extension.md` title to \"OAL Extension\"\n- Clear boundary: user docs (`concepts-and-designs/`) for script syntax, developer docs (`guides/`) for SPI implementation\n\n**5. Other improvements**\n\n- Extract `generateFilterTestBody()` shared by `compileFilter()` and `generateFilterSource()`\n- Fail fast on unknown inline expression types\n- Fix upload-artifact v4 conflict (`overwrite: true`)\n- Fix Node.js 20 deprecation (`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24\u003dtrue`, checkout v4→v6)"
    },
    {
      "commit": "0e46059cfea0221950825a94cc275fdfe04e49c2",
      "tree": "3303c1f581c71ff3598c71c9ab3a997f02194c1c",
      "parents": [
        "f7f2e97d7f7c2d9c356704b0f1b4ecd035062ea0"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Mar 26 19:58:54 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 26 19:58:54 2026 +0800"
      },
      "message": "MAL: add extension function SPI, proper numeric types in codegen (#13761)\n\n**1. Extension Function SPI (`namespace::method()` syntax)**\n\nAdds a pluggable extension mechanism for MAL using `::` separator syntax, enabling external modules\nto contribute MAL functions that compile into direct static method calls.\n\n- `MalFunctionExtension` SPI interface with `name()` returning namespace\n- `@MALContextFunction` annotation on **static** methods — auto-detected via reflection at startup\n- First parameter must be `SampleFamily` (auto-bound), return type must be `SampleFamily`\n- Non-static annotated methods throw `IllegalArgumentException` at startup\n- Duplicate namespace or method names throw `IllegalArgumentException` at startup\n- `m.getDeclaringClass()` used for accurate FQCN in codegen (not `ext.getClass()`)\n- `List` parameters validated as `List\u003cString\u003e` via generic type inspection\n- Supports `String`, `double`, `float`, `long`, `int`, `List\u003cString\u003e` parameter types\n- Direct static method call codegen — no reflection or registry dispatch at runtime\n\nExample MAL script:\n```\nmetric.sum([\u0027svc\u0027]).test::scale(3.0)\n```\nGenerated code:\n```java\nsf \u003d TestMalExtension.scale(sf, 3.0);\n```\n\n**2. Proper numeric types in MAL codegen**\n\nInteger-valued literals now emit `Long.valueOf(NL)` instead of `Double.valueOf(N.0)` in:\n- Binary arithmetic: `metric * 100` → `sf.multiply(Long.valueOf(100L))`\n- Built-in method args: `.multiply(100)` → `Long.valueOf(100L)`\n- Standalone number expressions: `SampleFamily.EMPTY.plus(Long.valueOf(100L))`\n\nFractional values still use `Double.valueOf()`. All 1268 v1-v2 comparison tests pass.\n\n**3. GenAI model matcher singleton**\n\n`GenAIModelMatcher.getInstance()` — lazy singleton initialized from `gen-ai-config.yml`.\n`GenAIProviderPrefixMatcher.build()` simplified to delegate to singleton (removed unused config param)."
    },
    {
      "commit": "f7f2e97d7f7c2d9c356704b0f1b4ecd035062ea0",
      "tree": "82c03f3226d8016f4e5767601d96acd8be3ebe86",
      "parents": [
        "ce2ba33a26a409f35c82f63e8a276fa24ddc5f37"
      ],
      "author": {
        "name": "Arda Çuhadaroğlu",
        "email": "2892332+ardacuhadaroglu@users.noreply.github.com",
        "time": "Thu Mar 26 11:41:49 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 26 16:41:49 2026 +0800"
      },
      "message": "Fix on-demand pod log timestamp parsing for non-UTC timezones (#13759)\n\nReplace the custom DateTimeFormatter with ISO_OFFSET_DATE_TIME, which natively handles both RFC3339 and RFC3339Nano (with or without fractional seconds, any timezone offset)"
    },
    {
      "commit": "ce2ba33a26a409f35c82f63e8a276fa24ddc5f37",
      "tree": "2927e5b9f574a7c7e7348adbcdd41b1ba6f3b9d2",
      "parents": [
        "5d85222bafb35565c2d19b1276f0cc14d6968e18"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Mar 26 11:10:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 26 11:10:28 2026 +0800"
      },
      "message": "Support model prefix matching and aliases for GenAI pricing (#13758)\n\n**Problem:** LLM providers return versioned model names in API responses (e.g., `gpt-4o-2024-08-06`, `claude-sonnet-4-20250514`), which don\u0027t match the exact model names in `gen-ai-config.yml` (e.g., `gpt-4o`, `claude-4-sonnet`). This causes cost estimation to fail silently.\n\n**Changes:**\n\n1. **Trie-based longest-prefix model matching** — replaces exact `HashMap.get()` lookup. Now `gpt-4o-2024-08-06` matches config entry `gpt-4o`, and `gpt-4o-mini-2024-07-18` correctly matches `gpt-4o-mini` (longer prefix wins).\n\n2. **Model aliases** — Anthropic uses reversed naming (`claude-sonnet-4`) vs config (`claude-4-sonnet`). Aliases allow one pricing entry to match both conventions:\n   ```yaml\n   - name: claude-4-sonnet\n     aliases: [claude-sonnet-4]\n   ```\n\n3. **Move shared utils to library-util** — `GenAIPricingConfig`, `GenAIPricingConfigLoader`, `GenAIModelMatcher` moved to `server-library/library-util` genai package for shared access by both agent-based analysis (gen-ai-analyzer) and future OTLP-based monitoring (meter-analyzer MAL scripts).\n\n4. **Anthropic aliases added** to `gen-ai-config.yml` for all Claude models (API response naming: `claude-{tier}-{version}` format)."
    },
    {
      "commit": "5d85222bafb35565c2d19b1276f0cc14d6968e18",
      "tree": "cb6b63fc90b93b84f439b93d02a5dd46dde0f385",
      "parents": [
        "28a6d0a327b443c2e1344ae4aef527c602222319"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Mar 26 09:50:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 26 09:50:56 2026 +0800"
      },
      "message": "Add SWIP-10: Support Envoy AI Gateway Observability (#13757)"
    },
    {
      "commit": "28a6d0a327b443c2e1344ae4aef527c602222319",
      "tree": "148af1c0109f2e76874bde098d1432eec8abc8ee",
      "parents": [
        "86b8c45082c7485e0090b38f2b60e89f19331eba"
      ],
      "author": {
        "name": "peachisai",
        "email": "2581009893@qq.com",
        "time": "Wed Mar 25 21:53:53 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 25 21:53:53 2026 +0800"
      },
      "message": "Support Virtual-GenAI monitoring (#13745)\n\n  Adds a VIRTUAL_GENAI observability layer for monitoring Generative AI service calls detected by agent plugins (e.g., Spring AI). Tracks traffic, latency, success rate, token usage, \n  time-to-first-token, and estimated cost.\n                                                                                                                                                                                       \n  Architecture                                                                                                                                                                         \n  - Provider \u003d Service, Model \u003d Instance — reuses ServiceMeta and ServiceInstance sources                                                                                              \n  - Two new scopes: GenAIProviderAccess (service-level) and GenAIModelAccess (instance-level) for GenAI-specific metrics                                                             \n  - Cost stored as long amplified by 10^6 to work with SumMetrics                                                                                                                      \n                                                                                                                                                                                       \n  New Module: gen-ai-analyzer                                                                                                                                                                                                                                                                                                                                            \n  - Extracts GenAI metrics from agent spans via semantic convention tags (gen_ai.*)                                                                                                    \n  - Prefix-based model→provider matching (e.g., gpt* → openai)                                                                                                                       \n  - Estimated cost calculation from configurable per-model pricing                                                                                                                     \n                                                                                                                                                                                       \n  Configuration                                                                                                                                                                                                                                                                                                                                                          \n  - gen-ai-config.yml — provider/model pricing for OpenAI, Anthropic, Gemini, Mistral, DeepSeek, ByteDance, Zhipu AI, Alibaba, Tencent, Moonshot, MiniMax, and Ollama (template)       \n   \n  OAL                                                                                                                                                                                                                                                                                                                                                                 \n  - virtual-gen-ai.oal with new grammar tokens for provider and model metrics aggregation                                                                                              \n   \n  UI                                                                                                                                                                                                                                                                                                                                                          \n  - Dashboard templates for provider-level and model-level views\n  - \"GenAI\" menu section placed before \"Self Observability\" in both docs/menu.yml and ui-initialized-templates/menu.yaml\n                                                                                                                                                                                       \n  Testing                                                                                                                                                                        \n  - Unit tests covering config loading, provider matching, and cost estimation                                                                                                         \n  - E2E test with mock LLM controller\n                                                                                                                                                                                       \n  Documentation                                                                                                                                                                      \n  - docs/en/setup/service-agent/virtual-genai.md — span contract, provider config, available metrics    "
    },
    {
      "commit": "86b8c45082c7485e0090b38f2b60e89f19331eba",
      "tree": "55a3a3d635db1bdce03a89d1de8a67105c3e38ca",
      "parents": [
        "35caa9791ea6b36a7b6a2c14343b7eda17353532"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Mar 23 22:36:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 23 22:36:46 2026 +0800"
      },
      "message": "Add GraalVM native image distribution documentation (#13755)"
    },
    {
      "commit": "35caa9791ea6b36a7b6a2c14343b7eda17353532",
      "tree": "86a02418e3126119d996586e57a3334e38e731eb",
      "parents": [
        "5806ff2f7c268e970f5488a8438d19bd2745e42e"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Mar 23 13:40:25 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 23 13:40:25 2026 +0800"
      },
      "message": "Use maven.compiler.release and bump BanyanDB e2e commit (#13754)\n\n* Use maven.compiler.release instead of source/target and bump BanyanDB e2e commit\n\n- Replace maven.compiler.source and maven.compiler.target with\n  maven.compiler.release\u003d11 in root pom.xml. This ensures the compiler\n  also validates against JDK 11 API surface (boot classpath enforcement).\n- Remove redundant compiler and encoding properties from\n  library-pprof-parser/pom.xml since they are inherited from the parent.\n- Bump SW_BANYANDB_COMMIT to latest main (3c0e3a78)."
    },
    {
      "commit": "5806ff2f7c268e970f5488a8438d19bd2745e42e",
      "tree": "b703d60c03fff1bd965c0c2cfd5a0a26ade216c9",
      "parents": [
        "4f706459e76201a2dd23cb83e4fd727b6c0337f0"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sun Mar 22 10:50:15 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Mar 22 10:50:15 2026 +0800"
      },
      "message": "Pin Docker GitHub Actions to Apache approved SHAs (#13753)\n\n* Pin Docker GitHub Actions to Apache approved SHAs\n\nReplace version tags with SHA pins from Apache infrastructure-actions\napproved_patterns.yml to comply with the enterprise action allow list.\n\n- docker/login-action: v3/v1.10.0 → c94ce9fb...\n- docker/setup-buildx-action: v3 → 8d2750c6...\n- docker/setup-qemu-action: v3 → 29109295...\n\n* Remove memory limits from OTLP traces e2e test services\n\nThe frontend was dropping 312K+ spans due to maxQueueSize reached,\nlikely caused by memory pressure from the 200M limit on Node.js.\nThe productcatalogservice had only 20M which is very tight for a\nGo service with gRPC + OTLP export."
    },
    {
      "commit": "4f706459e76201a2dd23cb83e4fd727b6c0337f0",
      "tree": "414de7b37f614a5a57279089130313ad97afe9b1",
      "parents": [
        "726ebcc321dbd3c258963fc4bc23d320b903f6d9"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 20 15:17:20 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 20 15:17:20 2026 +0800"
      },
      "message": "Fix OTLP traces e2e test stability (#13752)"
    },
    {
      "commit": "726ebcc321dbd3c258963fc4bc23d320b903f6d9",
      "tree": "9a91ded30b06c1b510e9daaa02be49e205f3560f",
      "parents": [
        "7751bafa92aa5c5ecb13fe8e579e3149ceffaa61"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Mar 19 16:44:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 19 16:44:59 2026 +0800"
      },
      "message": "MAL v2: replace LambdaMetafactory with companion class pattern for closures (#13750)\n\n**What changed:**\n\nClosure functional interface instances (`TagFunction`, `ForEachFunction`, `PropertiesExtractor`, `DecorateFunction`) were previously wired via `LambdaMetafactory` after class loading — requiring a static helper method on the main class and reflective `MethodHandles.lookup()` at runtime.\n\nNow each closure becomes a named companion class (e.g. `MainClass$_tag`) that **directly implements the functional interface with the closure body inlined** in the SAM method. The main class holds a `public static final` field initialized in a `static{}` block. No reflection, no `LambdaMetafactory`, no indirection.\n\n```java\n// Before: LambdaMetafactory wiring after class load\nclass MainClass implements MalExpression {\n    static Map _tag_apply(Map tags) { /* closure body */ }\n    TagFunction _tag;  // wired post-load via MethodHandles.lookup() + LambdaMetafactory\n}\n\n// After: closure body inlined directly in companion\u0027s SAM method\nclass MainClass implements MalExpression {\n    public static final TagFunction _tag \u003d new MainClass$_tag();\n}\nclass MainClass$_tag implements TagFunction {\n    public Object apply(Object _raw) {\n        Map tags \u003d (Map) _raw;\n        /* closure body lives here, verified by javap */\n        return tags;\n    }\n}\n```\n\n**Key fix:** `TagFunction` and `PropertiesExtractor` extend `Function\u003cMap,Map\u003e` whose erased SAM is `apply(Object)Object`, not `apply(Map)Map`. The companion SAM method uses `Object` parameter/return with a cast to avoid `AbstractMethodError` at runtime.\n\n**Removed dead code:** `addClosureMethod()`, `addStaticLocalVariableTable()`, the `isStatic` parameter on `addLocalVariableTable()`, and `generateCompanionMethod()`."
    },
    {
      "commit": "7751bafa92aa5c5ecb13fe8e579e3149ceffaa61",
      "tree": "a86f5c389dc7affa704457d7047d38ee4859cf5b",
      "parents": [
        "fbcaf236619d592ce282f5aa6d426a39423f86f0"
      ],
      "author": {
        "name": "Fine0830",
        "email": "fanxue0830@gmail.com",
        "time": "Thu Mar 19 09:38:10 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 19 09:38:10 2026 +0800"
      },
      "message": "sync ui (#13749)"
    },
    {
      "commit": "fbcaf236619d592ce282f5aa6d426a39423f86f0",
      "tree": "4cc25a043233b2aa285b37c825c806276885dfe1",
      "parents": [
        "32f611881d94d340a19dbd640c6a645a4382b0cb"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Mar 18 10:25:47 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 18 10:25:47 2026 +0800"
      },
      "message": "Fix LogBuilder tagsRawData to include LAL-added tags (#13748)\n\ntagsRawData only contained original LogData tags from the agent, causing LAL-added tags (via `tag \u0027key\u0027: value`) to be missing from the UI tag display and Kafka export. Merge both original and LAL-added tags into a single LogTags protobuf for tagsRawData."
    },
    {
      "commit": "32f611881d94d340a19dbd640c6a645a4382b0cb",
      "tree": "6207e3806f5791c534b54511931686192875f512",
      "parents": [
        "36bbb292500c7217f12cc162f2963e1da87d4ca5"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Mar 17 12:24:11 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 17 12:24:11 2026 +0800"
      },
      "message": "Fix MAL List.of() varargs for \u003e10 elements and split test class by scope (#13746)\n\n**Bug**: Javassist cannot resolve `List.of()` varargs overload for \u003e10 elements (JDK 11 only has fixed-arity overloads for 0–10). MAL expressions with `sum()` over 12 label keys (e.g., serviceRelation histogram percentile rules) fail to compile.\n\n**Fix**: Changed `StringListArgument` and `NumberListArgument` codegen from `List.of(...)` to `Arrays.asList(new T[]{...})`, which works for any number of elements.\n\n**Test refactoring**: Split `MALClassGeneratorTest` (41 tests) into 3 classes by scope:\n- `MALClassGeneratorTest` (18) — basic compilation, error handling, bytecode verification\n- `MALClassGeneratorClosureTest` (14) — tag/forEach closures, regex, network-profiling patterns\n- `MALClassGeneratorScopeTest` (9) — full-expression scope tests (endpoint, service, instance, serviceRelation)\n\nAll 68 tests pass."
    },
    {
      "commit": "36bbb292500c7217f12cc162f2963e1da87d4ca5",
      "tree": "5b6bc12d620ce36cb26cdf57cf7833ff52a42024",
      "parents": [
        "64a1795d8a582f2216f47bfe572b3ab649733c01"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sun Mar 15 11:44:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Mar 15 11:44:56 2026 +0800"
      },
      "message": "Automate release process with unified release.sh (#13744)\n\nReplaces the separate `create_release_tars.sh` and `start_next_version.sh` scripts with a single interactive `release.sh` that automates the full release workflow end-to-end:\n\n1. GPG signer verification (`@apache.org` required)\n2. Required tools check (`gpg`, `svn`, `shasum`, `git`, `yq`)\n3. Version detection from `pom.xml`\n4. Release/next version calculation (strip `-SNAPSHOT`, bump minor)\n5. Single git clone → build source/binary tarballs → GPG sign → SHA512\n6. Upload to SVN staging\n7. Vote email generation (sha512 checksums + submodule commit IDs auto-filled)\n8. Next version PR preparation (version bump, changelog rotation, menu.yml update)\n\nKey improvements over the old scripts:\n- **Single clone** instead of two separate clones\n- **No manual version export** — auto-detected from `pom.xml`\n- **Vote email fully populated** — submodule commit IDs resolved automatically\n- **SVN upload automated** — no manual folder creation/upload\n- **Interactive confirmations** for GPG signer and version numbers"
    },
    {
      "commit": "64a1795d8a582f2216f47bfe572b3ab649733c01",
      "tree": "fef421de0697f5cd79e510855c2e1c0c74f253ab",
      "parents": [
        "2709c94ab3cc8997fd45c2ff38f3059cb0120074"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 13 20:28:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 13 20:28:50 2026 +0800"
      },
      "message": "Fix LAL \u003d\u003d and !\u003d with numeric literals using object equality instead of numeric comparison (#13743)\n\nWhen comparing with numeric literals (e.g., `!\u003d 403`), the LAL compiler\nwas generating `Objects.equals()` with boxed types (`Long.valueOf(403L)`),\nwhich fails when the left side is a different boxed type (e.g., Integer).\nNow EQ/NEQ with numeric right-hand side use `generateNumericComparison()`\n(primitive `\u003d\u003d 403L` / `!\u003d 403L`), matching the behavior of `\u003e`, `\u003c`,\n`\u003e\u003d`, `\u003c\u003d`. String/boolean/null comparisons still use `Objects.equals()`.\n\nAlso ensures all LAL codegen unit tests both compile and assert generated\nsource (previously some tests only asserted source without compiling)."
    },
    {
      "commit": "2709c94ab3cc8997fd45c2ff38f3059cb0120074",
      "tree": "ddba225c0e15e3ce98dbc472de859e5213eee2f9",
      "parents": [
        "d8b97f8e5b5e0766217490915f2871167491b8fb"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 13 09:29:18 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 13 09:29:18 2026 +0800"
      },
      "message": "LAL: introduce LogMetadata to decouple metadata from LogData in pipeline entry (#13742)\n\n  - Introduce LogMetadata POJO as uniform metadata carrier, replacing LogData for metadata fields (service, layer, timestamp, trace context)                                   \n  - ExecutionContext uses metadata() + input() instead of log() + extraLog(); removes LogData type awareness from runtime                                                      \n  - Unify LogSinkListener.parse() to single 3-parameter signature (LogMetadata, Object, ExecutionContext)\n  - Envoy ALS receivers pass typed proto directly as input — no longer build fake LogData\n  - Separate test tool (LogTestQuery) from production sink via dryRun flag\n  - LogMetadata.TraceContext defaults to EMPTY (matching protobuf\u0027s never-null semantics), preventing NPE on untraced logs\n  - LogMetadataUtils.fromLogData(Builder) extracts fields directly without build()\n  - tag(\"KEY\") codegen is now input-type-aware: only intercepts for LogData.Builder input with single string literal arg; other cases fall through to normal type-oriented resolution"
    },
    {
      "commit": "d8b97f8e5b5e0766217490915f2871167491b8fb",
      "tree": "17c3f3377f34a687d4303566236e375058141531",
      "parents": [
        "58652507b633d8783b4d9dbfce609bccb5bd11ef"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Thu Mar 12 15:48:44 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 12 15:48:44 2026 +0800"
      },
      "message": "LAL: move service resolution into LALOutputBuilder, fix tag assignment restriction (#13741)\n\n  1. Refactor LALOutputBuilder.init() to take ModuleManager instead of                                                                                                           \n     NamingControl. Each builder (LogBuilder, DatabaseSlowStatementBuilder,                                                                                                    \n     SampledTraceBuilder, EnvoyAccessLogBuilder) now resolves its own\n     services and caches them in static fields. RecordSinkListener no\n     longer holds NamingControl, searchableTagKeys, or instanceof checks.\n\n  2. Fix bug where LAL tag assignments were incorrectly restricted to\n     LogBuilder via isAssignableFrom check, blocking any other output\n     builder from defining addTag(String, String)."
    },
    {
      "commit": "58652507b633d8783b4d9dbfce609bccb5bd11ef",
      "tree": "f07ad5645024b6331c8cc009bc3b9daad1f4d1b9",
      "parents": [
        "73d422f0d68dbbfa04fe42868b5aee0d35397654"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Mar 11 20:05:57 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 20:05:57 2026 +0800"
      },
      "message": "Fix K8s coordinator self-endpoint race condition (#13740)\n\n#### Root Cause\n\nThe previous fix (#13577) correctly resolved the self pod IP from the K8s API, but missed a **listener notification gap**. The self pod was excluded from the `DynamicEndpointGroup` endpoint list, so when the informer synced self after startup, the list didn\u0027t change — Armeria deduplicated and **never re-fired the listener**, leaving `remoteInstances` permanently stuck with `127.0.0.1`.\n\n#### Changes\n\n1. **`KubernetesLabelSelectorEndpointGroup`** — Include self pod in the endpoint list passed to `setEndpoints()`, so that when self appears in the informer, the endpoint list actually changes and triggers listener notification.\n\n2. **`KubernetesCoordinator`** — Instead of appending self separately, identify self from the endpoint list by matching against `getSelfEndpoint()`. Falls back to `127.0.0.1` only if self hasn\u0027t appeared yet (initial race window).\n\n3. **`SelfEndpointAccessor`** — New package-private interface extracted from the `instanceof` check, enabling test doubles without a real K8s client.\n\n4. **`KubernetesCoordinatorTest`** — 3 unit tests simulating the race condition:\n   - `shouldFallbackTo127WhenSelfNotSynced` — informer not synced yet\n   - `shouldResolveSelfAfterInformerSync` — self transitions from 127.0.0.1 to real IP\n   - `shouldElectTTLLeaderCorrectlyWithRealIPs` — TTL leader election with real IPs"
    },
    {
      "commit": "73d422f0d68dbbfa04fe42868b5aee0d35397654",
      "tree": "a6cf1d2115f53e2f45b1fff9f7acb748971c4ae9",
      "parents": [
        "f7bed0a74e6be2e3eacca881ee7a5c046f103642"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Mar 11 14:09:01 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 14:09:01 2026 +0800"
      },
      "message": "Update Istio E2E test versions to cover upstream supported releases (#13738)\n\n- Remove EOL Istio 1.20.0 from ALS and Metrics Service tests\n- Add Istio 1.25.0, 1.26.0, 1.27.0, 1.28.0, 1.29.0 to ALS, Metrics Service, and Ambient ALS tests\n- Update Rover with Istio Process test from 1.15.0 to 1.28.0\n- Update Rover Kubernetes version from 1.25 to 1.28 (reuse `kind.k28.yaml` from other Istio tests)"
    },
    {
      "commit": "f7bed0a74e6be2e3eacca881ee7a5c046f103642",
      "tree": "88f52a94690147ad00a27f4f513907c347176202",
      "parents": [
        "93bcd9415b2e4c747e94e2afd547e1041bb1de02"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Mar 11 10:39:02 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 10:39:02 2026 +0800"
      },
      "message": "LAL: add def local variable support with toJson/toJsonArray and type cast (#13737)\n\n**Grammar**: `def varName \u003d expression` with optional `as TypeName` cast (built-in types or FQCN).\n\n**Built-in functions**: `toJson()` converts String/Map/protobuf `Struct` to Gson `JsonObject`; `toJsonArray()` converts to `JsonArray`.\n\n**Type inference**: Variable type inferred at compile time from initializer expression. Supports null-safe navigation (`?.`) and method chaining on the inferred type. Optional `as` cast narrows type when the inferred type is too general (e.g., `Object` from generic API).\n\n**Runtime helpers**: `LalRuntimeHelper.toJsonObject()` overloads for String, Map, and protobuf Struct (recursive field conversion).\n\n**Test reorganization**: Split monolithic `LALClassGeneratorTest` (641 lines) into 5 scoped test classes:\n- `LALClassGeneratorBasicTest` (10 tests)\n- `LALClassGeneratorConditionTest` (10 tests)\n- `LALClassGeneratorExtractorTest` (10 tests)\n- `LALClassGeneratorDefTest` (10 tests)\n- `LALClassGeneratorSinkTest` (5 tests)\n\nPlus shared `LALClassGeneratorTestBase` with `.class` file output to `target/lal-generated-classes/` and `{TestClass}_{testMethod}` naming.\n\n**ALS-specific tests**: `EnvoyAlsLalTest` in envoy receiver module — 3 tests for JWT extraction from proto `filter_metadata` via chained `def` variables."
    },
    {
      "commit": "93bcd9415b2e4c747e94e2afd547e1041bb1de02",
      "tree": "ebc6af7df4974eee9e822c79de78141c551c56f9",
      "parents": [
        "13a8bf482a6905006b53bd9e158d2117693a473a"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Tue Mar 10 20:46:12 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 10 20:46:12 2026 +0800"
      },
      "message": "Add some banyandb e2e cases(cold query) back (#13734)"
    },
    {
      "commit": "13a8bf482a6905006b53bd9e158d2117693a473a",
      "tree": "7933fa4c60caee89f3555bb2a98e591207b4fac5",
      "parents": [
        "247583be0bf1730941c65af5dc7e330a46ad4656"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Mar 10 17:34:14 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 10 17:34:14 2026 +0800"
      },
      "message": "LAL: generalize extraLog type, add EnvoyAccessLogBuilder, and DSL class generator test (#13736)\n\n### Follow-up improvements to LAL v2 outputType mechanism (#13733)\n\nThese changes fix several issues discovered after the initial outputType PR and add a DSL class generator test for offline validation.\n\n**Key changes:**\n\n1. **Generalize `extraLog` from `Message` to `Object`** — `ILogAnalyzerService.doAnalysis()` and the entire listener chain now use `Optional\u003cObject\u003e` instead of `com.google.protobuf.Message`. This removes the protobuf coupling and allows non-protobuf input types in the future.\n\n2. **`LALOutputBuilder.init()` accepts `Object` instead of `LogData`** — Each builder casts to its expected input type internally (e.g., `LogData` for standard logs, `HTTPAccessLogEntry` for envoy). This enables the envoy access log builder to receive the raw protobuf entry directly.\n\n3. **Add `EnvoyAccessLogBuilder`** — A new `LALOutputBuilder` implementation for envoy access logs that extends `LogBuilder` and serializes the raw access log entry as JSON content. Registered via SPI and declared as the default output type for the MESH layer in `EnvoyHTTPLALSourceTypeProvider`.\n\n4. **Move `addTag` off `LALOutputBuilder` interface** — `addTag()` was a default no-op on the interface, silently dropping tags for non-Log output types. Now it only exists on `LogBuilder`, and the LAL compiler validates at compile time that `tag` assignments are only used with LogBuilder-compatible output types.\n\n5. **Remove dead `LogSinkListenerFactory` methods** — `addSinkListenerFactory()` and `getSinkListenerFactory()` on `ILogAnalysisListenerManager` were unused after the v2 refactoring.\n\n6. **Remove unused `ModuleManager`/`ModuleConfig` from `LogAnalyzer`** — Constructor now only takes `ILogAnalysisListenerManager`.\n\n7. **Add `DSLClassGeneratorTest`** — A test in `server-starter` that compiles all OAL, MAL, LAL, and Hierarchy scripts and dumps `.class` files to `target/generated-dsl-classes/` for offline inspection. Documents the tool in the debugging guide."
    },
    {
      "commit": "247583be0bf1730941c65af5dc7e330a46ad4656",
      "tree": "a12c3503f1d66dc6d5e07533bbc1230536b8ad5d",
      "parents": [
        "438334eb3e247264e443feaead227ff51dda9923"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Mar 09 11:28:13 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 09 11:28:13 2026 +0800"
      },
      "message": "Reduce noisy log levels in health checker and batch queue (#13735)\n\n- Health check: log at debug when healthy, warn when unhealthy\n  (was info on every request regardless of status)\n- BatchQueue rebalance: downgrade to debug (routine operation)"
    },
    {
      "commit": "438334eb3e247264e443feaead227ff51dda9923",
      "tree": "dabc4ca6868ffe71cd4032eaa4c3e8b43667e4e6",
      "parents": [
        "c626093b2a29e2b9c52eaa3c83db382061022f9b"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Mon Mar 09 09:24:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 09 09:24:49 2026 +0800"
      },
      "message": "LAL: replace slowSql/sampledTrace sub-DSLs with unified outputType mechanism (#13733)\n\n  LAL: replace slowSql/sampledTrace sub-DSLs with unified outputType mechanism\n\n  Remove hardcoded `slowSql {}` and `sampledTrace {}` grammar blocks from LAL.\n  Output types are now configured per-rule via `outputType` in YAML config, with\n  SPI-based short name resolution (e.g., `outputType: SlowSQL`).\n\n  Key changes:\n  - LALOutputBuilder SPI interface — builders register by name(), resolved at boot\n  - DatabaseSlowStatementBuilder and SampledTraceBuilder as SPI implementations\n  - LogBuilder as default output type, replacing inline parseLog() logic\n  - Output fields are regular extractor assignments, validated against builder\n    setters at compile time (missing setter \u003d compile error at boot)\n  - Rename ExtractorSpec → MetricExtractor (metrics-only responsibility)\n  - Fix init() overwriting extractor values, enum case-sensitivity, missing layer\n\n  All bundled LAL scripts (envoy-als, mesh-dp, k8s-service, *-slowsql) and\n  checker test cases updated. Grammar simplified by removing special-purpose blocks."
    },
    {
      "commit": "c626093b2a29e2b9c52eaa3c83db382061022f9b",
      "tree": "4580815e85d2b6b814d743d20ed5109e3de3ebec",
      "parents": [
        "1f08f9587c5a63a42ea492c919dbcf7d98bdb1ed"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sat Mar 07 11:18:48 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Mar 07 11:18:48 2026 +0800"
      },
      "message": "Fix v2 MAL compiler: sanitizeName, filter class generation, Groovy truth (#13732)\n\n1. `sanitizeName` lost digits for numeric-prefixed metric names\n`sanitizeName(\"4xx\")` produced `_xx` (replacing `4` with `_`) instead of `_4xx` (prepending `_`). Fixed in `MALCodegenHelper`, `LALCodegenHelper`, and `HierarchyRuleClassGenerator` to prepend `_` and keep all valid identifier-part characters.\n\n2. Generated class naming: `{yamlFileName}_L{lineNo}_{ruleName}`\nAll three generators (`MALClassGenerator`, `LALClassGenerator`, `HierarchyRuleClassGenerator`) now use a `buildHintedName()` method that combines `yamlSource` (file name + line number) with `classNameHint` (rule name) into deterministic class names for stack trace traceability.\n\nExamples:\n- MAL expression: `vm_L25_cpu_total_percentage`\n- MAL filter: `gateway_service_L33_filter`\n- LAL: `execution_basic_L110_if_else_if_warn`\n- Hierarchy: `test_hierarchy_definition_L88_name`\n\nFalls back to `MalExpr_\u003cN\u003e` / `LalExpr_\u003cN\u003e` / `HierarchyRule_\u003cN\u003e` when no hint is set.\n\n3. v2 filter `.class` generation in checker tests\nThe v1-v2 checker now compiles filter expressions and writes `.class` files to `generated-classes/` directories (56 filter classes). Production `FilterExpression` accepts `yamlSource` parameter for consistent class naming.\n\n4. SourceFile attributes with YAML traceability\nGenerated `.class` files now include the YAML filename and line number in the `SourceFile` attribute for both expression and filter classes (e.g., `(gateway-service.yaml:33)filter.java`).\n\n5. Groovy truth semantics for filter closures\nv2 filter codegen only checked `!\u003d null` for standalone expressions in boolean context (e.g., `tags.ApiId || tags.ApiName`), but Groovy truth also requires non-empty string check. Added `MalRuntimeHelper.isTruthy()` runtime helper: `null → false`, empty string → `false`, `Boolean.FALSE → false`.\n\n6. LAL and Hierarchy checker enhancements\n- `LalComparisonTest`: added `lineNo` to `LalRule`, set `yamlSource` with file name + line number\n- `HierarchyRuleComparisonTest`: set `yamlSource` with file name + line number, generates `.class` files\n- LAL test data files renamed from `.input.data` to `.data.yaml` for consistency with MAL\n\n7. Documentation updates\n- `dynamic-code-generation-debugging.md`: updated class name examples, SourceFile format table, error trace examples\n- `changes.md`: added entries for class naming, filter compilation, sanitizeName fix\n- MAL/LAL/Hierarchy `CLAUDE.md`: updated Package \u0026 Class Naming sections"
    },
    {
      "commit": "1f08f9587c5a63a42ea492c919dbcf7d98bdb1ed",
      "tree": "b8bd33e5f6b288906be0cae5d53f073d8fe2f439",
      "parents": [
        "c55f57673d583d308cd567fa97e2d1033585632d"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 06 16:32:21 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 06 16:32:21 2026 +0800"
      },
      "message": "Activate otlp-traces handler in receiver-otel by default (#13731)"
    },
    {
      "commit": "c55f57673d583d308cd567fa97e2d1033585632d",
      "tree": "886e78aed2e8f6e7f756f3987948d1b28aba2cc9",
      "parents": [
        "b106892f20a1136713986278be17ecf53279831e"
      ],
      "author": {
        "name": "Senrian",
        "email": "47714364+Senrian@users.noreply.github.com",
        "time": "Fri Mar 06 13:59:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 06 13:59:37 2026 +0800"
      },
      "message": "fix: replace Collections.EMPTY_* with empty*() to avoid unchecked warnings (#13724)"
    },
    {
      "commit": "b106892f20a1136713986278be17ecf53279831e",
      "tree": "95dd5f38ed025d4835fbb5bf3a5d3ecf19736402",
      "parents": [
        "5a3f6260e4dd681a9132204e5299064bef079886"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Mar 06 11:16:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 06 11:16:59 2026 +0800"
      },
      "message": "Introduce MAL/LAL/Hierarchy V2 engine — replace Groovy DSL runtime with ANTLR4 + Javassist (#13723)\n\nReplace the Groovy-based DSL runtime for MAL (Meter Analysis Language), LAL\n(Log Analysis Language), and Hierarchy matching rules with compile-time ANTLR4\nparsing and Javassist bytecode generation — extending the same approach used\nby the OAL V2 engine (#13699) to all remaining DSL compilers.\n\nAll three compilers follow the same pipeline:\n\n  DSL string → ANTLR4 parse → Immutable AST → Javassist bytecode → Direct Java execution\n\n## Why\n\n- Remove Groovy runtime dependency (~7 MB) from OAP server classpath\n- Eliminate runtime interpretation — generated bytecode uses direct method\n  calls with zero reflection at runtime\n- Thread-safe by design — generated instances are stateless singletons;\n  per-request state passed as explicit parameters (no ThreadLocal, no\n  mutable wrappers)\n- Fail-fast at boot — DSL compilation errors caught during startup with\n  file/line/column reporting, not at first log/metric arrival\n- Debugger-friendly — all generated methods include LocalVariableTable (LVT)\n  entries with named variables\n\n## Architecture\n\n  | DSL       | Compiled Interface                    | Runtime Signature                            |\n  |-----------|---------------------------------------|----------------------------------------------|\n  | MAL       | MalExpression                         | SampleFamily run(Map\u003cString, SampleFamily\u003e)  |\n  | LAL       | LalExpression                         | void execute(FilterSpec, ExecutionContext)    |\n  | Hierarchy | BiFunction\u003cService, Service, Boolean\u003e | Boolean apply(Service, Service)              |\n\nAll v2 classes live under `.v2.` packages to avoid FQCN conflicts with v1\n(Groovy) classes. v1 code is preserved in test/script-cases/script-runtime-with-groovy/\nfor cross-version comparison testing only — it is no longer used at runtime.\n\n### MAL Compiler\n\n- ANTLR4 grammar: MALLexer.g4 (132 lines), MALParser.g4 (282 lines)\n- Parser: MALScriptParser.java (888 lines) → immutable MALExpressionModel AST\n- Codegen: MALClassGenerator.java (1,334 lines) + MALClosureCodegen.java (783 lines)\n- Closures compiled as methods on the main class, wrapped via LambdaMetafactory\n  (same mechanism javac uses for lambdas — JIT-inlineable, no separate .class files)\n- Supports regex match (\u003d~), def type inference, GString interpolation, time() scalar,\n  decorate() bean mode, ternary expressions, unary minus, null arguments\n\n### LAL Compiler\n\n- ANTLR4 grammar: LALLexer.g4 (175 lines), LALParser.g4 (476 lines)\n- Parser: LALScriptParser.java (947 lines) → immutable LALScriptModel AST\n- Codegen: LALClassGenerator.java (553 lines) + LALBlockCodegen.java (1,157 lines)\n- Single flat class per rule (no closures, no sub-classes)\n- LalRuntimeHelper: toStr() preserves null (unlike String.valueOf → \"null\"),\n  mapVal() for JSON parsed map access, proto getter chain resolution\n- LALSourceTypeProvider SPI resolves extraLogType proto class at compile time;\n  proto getter chains resolved via reflection at compile time, emitted as direct\n  method calls with null-safe ternary chains at runtime\n\n### Hierarchy Compiler\n\n- ANTLR4 grammar: HierarchyRuleLexer.g4 (105 lines), HierarchyRuleParser.g4 (135 lines)\n- Parser: HierarchyRuleScriptParser.java (375 lines) → immutable HierarchyRuleModel AST\n- Codegen: HierarchyRuleClassGenerator.java (543 lines)\n- CompiledHierarchyRuleProvider registered via SPI as HierarchyRuleProvider\n- Property access → getter methods, \u003d\u003d → Objects.equals(), supports if/else/return blocks\n\n## v1-v2 Cross-Version Verification\n\nEvery production DSL expression is run through both Groovy v1 and Javassist v2,\nthen asserted for identical results. Both v1 and v2 must pass — v1 failure also\nfail()s (no silent skip).\n\n  | DSL                  | Expressions | Source                                      |\n  |----------------------|-------------|---------------------------------------------|\n  | MAL expressions      | 1,229       | 73 YAML files across 6 directories          |\n  | MAL filter closures  | 31          | Separate MalFilterComparisonTest             |\n  | LAL scripts          | 29          | test-lal/ directory                          |\n  | Hierarchy rules      | 4 rules     | test-hierarchy-definition.data.yaml          |\n  | Total                | ~1,290+     |                                              |\n\nMAL comparison validates: samples, scopeType, downsampling, isHistogram,\nscopeLabels, aggregationLabels, plus runtime Sample[] output arrays.\nLAL comparison validates: shouldAbort, shouldSave, log.service,\nlog.serviceInstance, log.endpoint, log.layer, log.timestamp, log.tags.\n\n76 companion .data.yaml files provide realistic SampleFamily mock input.\n10 .input.data files provide proto-json LogData/extraLog mock data for LAL.\n\n## Performance (JMH benchmarks)\n\n  | DSL       | Operation | v2 speedup over v1 |\n  |-----------|-----------|--------------------|\n  | MAL       | execute   | ~6.8x faster       |\n  | LAL       | compile   | ~39x faster        |\n  | LAL       | execute   | ~2.8x faster       |\n  | Hierarchy | execute   | ~2.6x faster       |\n\n## Generated .class File Debugging\n\nSet SW_DYNAMIC_CLASS_ENGINE_DEBUG to any non-empty value to dump .class files:\n\n  | DSL       | Output directory              |\n  |-----------|-------------------------------|\n  | OAL       | oal-rt-generated-classes/     |\n  | MAL       | mal-generated-classes/        |\n  | LAL       | lal-generated-classes/        |\n  | Hierarchy | hierarchy-generated-classes/  |\n\nRenamed from SW_OAL_ENGINE_DEBUG → SW_DYNAMIC_CLASS_ENGINE_DEBUG to cover all\nfour DSL compilers. Documentation: docs/en/operation/dynamic-code-generation-debugging.md\n\n## New Maven Modules\n\n- oap-server/analyzer/hierarchy/ — Hierarchy rule v2 compiler + SPI provider\n- test/script-cases/script-runtime-with-groovy/ — Test aggregator with 6 sub-modules:\n  mal-v1-with-groovy, lal-v1-with-groovy, hierarchy-v1-with-groovy (v1 runtimes),\n  mal-lal-v1-v2-checker, hierarchy-v1-v2-checker (cross-version comparison tests)\n\n## Other Changes\n\n- Remove initExp from MAL configuration — was internal Groovy startup validation;\n  v2 compiler performs fail-fast validation at startup natively\n- Update hierarchy rule documentation — rules now use a dedicated expression grammar\n  (property access, String methods, if/else, comparisons, logical operators) instead\n  of Groovy scripts. All shipped rules are fully compatible.\n- Remove PowerMock dependency — replaced with ReflectUtil (standard Java reflection\n  + sun.misc.Unsafe for final fields) to support JDK 25+\n- Upgrade Byte Buddy to 1.18.7 — configure explicit -javaagent for Mockito/Byte Buddy\n  in Surefire to avoid JDK 25 dynamic agent loading warnings\n- Relocate JMH benchmarks from oap-server/microbench/ to target modules\n- Add CLAUDE.md AI assistant guides for meter-analyzer, log-analyzer, hierarchy, oal-rt\n- Add Claude Code skills: compile, test, license, gh-pull-request, ci-e2e-debug,\n  run-e2e, generate-classes\n\n## Documentation\n\n- docs/en/academy/dsl-compiler-design.md — Architecture overview of all 4 DSL compilers\n- docs/en/operation/dynamic-code-generation-debugging.md — .class dump guide\n- docs/en/changes/changes.md — Changelog entries\n- docs/en/setup/backend/configuration-vocabulary.md — SW_DYNAMIC_CLASS_ENGINE_DEBUG\n- Updated: lal.md, service-hierarchy.md, service-hierarchy-configuration.md,\n  backend-meter.md, backend-zabbix.md, oal.md\n\nReviewed-by: Gemini Code Review (concurrency, thread-safety, feature parity validated)\nVerified-by: Gemini Verification Report (100% script coverage, all branch paths exercised)\nApproved-by: wankai123"
    },
    {
      "commit": "5a3f6260e4dd681a9132204e5299064bef079886",
      "tree": "5c95ce6a63e267554ef3915fc39b333f48e1fffd",
      "parents": [
        "d966678b4b17b8f91c1ad1bdd853897f8735728b"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Thu Mar 05 21:28:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 21:28:38 2026 +0800"
      },
      "message": "Fix `/debugging/config/dump` may leak sensitive information if there are second level properties in the configuration. (#13726)\n\n"
    },
    {
      "commit": "d966678b4b17b8f91c1ad1bdd853897f8735728b",
      "tree": "aa51851f4b92ab78011870e28da448a657bc04bb",
      "parents": [
        "4325e74919d0bf8496e9d861e3bbde99cffff597"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Thu Mar 05 11:27:13 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 11:27:13 2026 +0800"
      },
      "message": "Support TraceQL and Tempo API for Zipkin trace query. (#13719)\n\n"
    },
    {
      "commit": "4325e74919d0bf8496e9d861e3bbde99cffff597",
      "tree": "c71f814d039827ab8006740d7fa8a28a5c4b93bd",
      "parents": [
        "35c4a2422cafc04f217d89fbf4001ffc6ef8eaa1"
      ],
      "author": {
        "name": "kezhenxu94",
        "email": "kezhenxu94@apache.org",
        "time": "Tue Mar 03 17:07:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 03 17:07:46 2026 +0800"
      },
      "message": "chore: bump up some dependencies (#13721)\n\n"
    },
    {
      "commit": "35c4a2422cafc04f217d89fbf4001ffc6ef8eaa1",
      "tree": "8c9f7801ec8bb763d6c6165c6388b6ee98164dc9",
      "parents": [
        "95b6a197c436bd35f134abb4914cf315e6ed8b2e"
      ],
      "author": {
        "name": "Zixin Zhou",
        "email": "zhouzixin@apache.org",
        "time": "Sat Feb 28 17:01:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Feb 28 17:01:34 2026 +0800"
      },
      "message": "chore(build): bump up netty to 4.2.10.Final (#13717)\n\n"
    },
    {
      "commit": "95b6a197c436bd35f134abb4914cf315e6ed8b2e",
      "tree": "42cec0c295bc4bb88d5d445e652917710f84f1c9",
      "parents": [
        "1caee8e7bbf6de2cfb8aca4df6ce726362175b48"
      ],
      "author": {
        "name": "peachisai",
        "email": "2581009893@qq.com",
        "time": "Fri Feb 27 15:10:03 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 27 15:10:03 2026 +0800"
      },
      "message": "Add the spring-ai components and the GenAI layer. (#13712)\n\n"
    },
    {
      "commit": "1caee8e7bbf6de2cfb8aca4df6ce726362175b48",
      "tree": "0bdc02b2d8bb393ec7b99a751ebc23e817c26bd8",
      "parents": [
        "92a8f5d3c1ad2c904fd51cb243548544658b11b3"
      ],
      "author": {
        "name": "Hm",
        "email": "100026743+Hyeon-moGu@users.noreply.github.com",
        "time": "Fri Feb 27 15:13:04 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 27 14:13:04 2026 +0800"
      },
      "message": "Refactor: Replace Whitebox with standard reflection in library, core and configuration (#13713)\n\n"
    },
    {
      "commit": "92a8f5d3c1ad2c904fd51cb243548544658b11b3",
      "tree": "697b1a56306b9576d693980360031583602be49b",
      "parents": [
        "a0cec0ca237792497d2da0b65757d11f58c3f342"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Feb 27 12:27:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 27 12:27:50 2026 +0800"
      },
      "message": "Drop ES 7 (EOL) and upgrade all Elasticsearch E2E tests to 8.18.8 (#13715)\n\n* Drop ES 7 (EOL) and upgrade all Elasticsearch E2E tests to 8.18.8\n* Add xpack.security.enabled\u003dfalse for ES 8 docker-compose files\n\nES 8 enables security by default, unlike ES 7. The 12 docker-compose\nfiles that were upgraded from ES 7.15.0 to 8.18.8 need this setting\nexplicitly disabled for the E2E tests to connect without authentication.\n\n* Install ECK operator before SkyWalking helm chart for ES-based K8s tests\n\nThe new skywalking-helm chart uses ECK (Elastic Cloud on Kubernetes) CRDs\nto deploy Elasticsearch, replacing the old StatefulSet-based approach.\nThe ECK operator must be installed first so the Elasticsearch CRD is\navailable when the skywalking chart is applied.\n\nFor each ES-based K8s E2E test:\n- Add \"Install ECK operator\" step that pulls the chart, extracts the\n  bundled eck-operator subchart, installs it, and waits for readiness\n- Add --set eckOperator.enabled\u003dfalse to skip the bundled operator\n  (already installed separately)\n- Remove obsolete elasticsearch.replicas/minimumMasterNodes settings\n- Add `helm dep up skywalking-helm` after `helm pull --untar` in all 13\n  Helm-based e2e.yaml files. The OCI chart package does not include\n  pre-packaged subchart .tgz files in charts/, so `helm dep up` is\n  needed to download the eck-operator subchart dependency before\n  installing it.\n- Remove OpenSearch 1.x test entries (Storage 1.3.10, Trace Profiling\n  1.1.0/1.3.6, Zipkin 1.2.0, UI Menu 1.1.0/1.3.6) from CI workflow.\n  Keep OpenSearch 2.x and 3.x tests."
    },
    {
      "commit": "a0cec0ca237792497d2da0b65757d11f58c3f342",
      "tree": "8119ed8866e19d261a861942419d648308e0b06b",
      "parents": [
        "aa9fe105567fdbe2f58a67f3d0a755ced2ae90e0"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Wed Feb 25 12:21:15 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Feb 25 12:21:15 2026 +0800"
      },
      "message": "Add QueueUsageBenchmark to validate BatchQueueStats under backpressure (#13710)\n\n"
    },
    {
      "commit": "aa9fe105567fdbe2f58a67f3d0a755ced2ae90e0",
      "tree": "ac00d898dfe702636396f140188f9941e9f5e50b",
      "parents": [
        "83b82ef1518497cb218127db30b9583ac0d81acb"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Wed Feb 25 11:25:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Feb 25 11:25:30 2026 +0800"
      },
      "message": "Make so11y `metrics instance_metrics_aggregation_queue_used` in per ten thousand precision. (#13711)\n\n"
    },
    {
      "commit": "83b82ef1518497cb218127db30b9583ac0d81acb",
      "tree": "603cd28bc36b793ac605db0f0151994a36eb0a70",
      "parents": [
        "46c16259cdba713522a20dc41db2169a54cc1435"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Tue Feb 24 10:36:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Feb 24 10:36:45 2026 +0800"
      },
      "message": "Replace DataCarrier with BatchQueue for metrics pipeline (#13703)\n\n  What it does\n\n  This introduces a new library-batch-queue module and uses it to replace the legacy DataCarrier across the OAP metrics pipeline. It also includes several thread optimizations\n   changes discovered during benchmarking.\n\n  Key changes by category\n\n  1. New queue infrastructure (library-batch-queue)\n  - Partitioned, self-draining queue with type-based dispatch\n  - Adaptive partitioning (grows with registered handlers)\n  - Idle backoff (no busy-waiting)\n  - Hardware-aware thread sizing (cpuCores(), cpuCoresWithBase(), fixed())\n  - Throughput-weighted drain rebalancing (DrainBalancer)\n\n  2. DataCarrier → BatchQueue migration\n  - L1 aggregation, L2 persistence, TopN, exporters, gRPC remote client all migrated\n  - OAL + MAL unified into shared queues (was separate pools)\n  - Thread count: 36 → 15 on 8-core machine\n  - library-datacarrier-queue module removed entirely\n\n  3. Thread leak fixes\n  - HttpAlarmCallback: new HttpClient per post() call leaked NIO selector threads → shared singleton\n  - KubernetesCoordinator: client never closed, selector thread persisted → SharedKubernetesClient singleton (9 call sites → 1 shared instance)\n\n  4. Server thread optimizations\n  - Armeria HTTP event loop: cores * 2 per server (140 on 10-core) → min(5, cores) shared across all 7 servers\n  - Virtual threads (JDK 25+) for gRPC handlers, HTTP blocking executor, and 6 scheduler threads\n  - Named ThreadFactory for all anonymous Executors pools\n\n  5. Benchmarking infrastructure\n  - benchmarks/ directory with Kind-based K8s environments\n  - Thread dump collection and analysis tooling\n  - First case: thread-analysis on Istio + BanyanDB cluster\n\n  Overall result: ~50% thread reduction (150+ → ~72 on 10-core JDK 25+ benchmark)\n"
    },
    {
      "commit": "46c16259cdba713522a20dc41db2169a54cc1435",
      "tree": "d78a9bdef2e4fe55658a78b11ff75d8bd73570e4",
      "parents": [
        "b39805b206b1dede4f7f358c0158cd55457184cd"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sun Feb 22 19:33:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Feb 22 19:33:38 2026 +0800"
      },
      "message": "Add virtual thread support for gRPC and HTTP servers on JDK 25+ (#13705)\n\n  Add virtual thread support for gRPC and Armeria HTTP servers on JDK 25+\n\n  Enable virtual-thread-per-task executors for all 4 gRPC and 7 HTTP\n  server handler pools when running on JDK 25+, with automatic fallback\n  to platform thread pools on older JDKs. JDK 25 is required as the\n  first LTS with the synchronized pinning fix (JEP 491).\n\n  Key changes:\n  - VirtualThreads: reflection-based detection and executor factory\n  - VirtualThreadScheduledExecutor: virtual-thread-backed scheduled\n    executor for Armeria\u0027s blockingTaskExecutor\n  - Default Docker image changed to JDK 25; JDK 11/17/21 variants kept\n  - Kill switch: SW_VIRTUAL_THREADS_ENABLED\u003dfalse\n\n  On JDK 25+, ~9 ForkJoinPool carrier threads replace up to 800+\n  platform threads across all handler pools.\n"
    },
    {
      "commit": "b39805b206b1dede4f7f358c0158cd55457184cd",
      "tree": "29ceed4d893f704482d0d1e9c8b3542f87915b96",
      "parents": [
        "88ca64875914613014cb0e99845d7a02b874a680"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sat Feb 21 18:23:58 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Feb 21 18:23:58 2026 +0800"
      },
      "message": "Upgrade infra-e2e to fix Docker 29 containerd compatibility (#13708)\n\n"
    },
    {
      "commit": "88ca64875914613014cb0e99845d7a02b874a680",
      "tree": "62869d3b695084c15fb09ba7a4dd4f3892686e91",
      "parents": [
        "b5378919288e4e7992a0a794fa0126ff29a42702"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Sat Feb 21 17:17:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Feb 21 17:17:30 2026 +0800"
      },
      "message": "Fix CI E2E failures caused by Docker 29 on GitHub Actions runners (#13706)\n\nGitHub Actions runners upgraded to Docker 29, which enables containerd image store by default. This causes two problems:\n\n1. **containerd v2.1.5 lowered the default file descriptor limit** from 1,048,576 to 1,024, causing applications like Elasticsearch to crash immediately at startup (ES requires at least 65,536).\n2. **Docker 29 raised the minimum API version to 1.44**, while the current `skywalking-infra-e2e` tool uses Docker SDK API v1.41, causing `client version 1.41 is too old` errors.\n\nThis PR fixes both issues by:\n- Disabling the containerd image store (`containerd-snapshotter: false`) in all Docker-related CI jobs, restoring the classic overlay2 storage driver and its default ulimits.\n- Exporting `DOCKER_API_VERSION` from the server\u0027s supported version so the Docker SDK client negotiates correctly after restart.\n\nAffected jobs: `docker` (image build), `e2e-test`, `e2e-test-istio`, `e2e-test-istio-ambient`, `e2e-test-java-versions`."
    },
    {
      "commit": "b5378919288e4e7992a0a794fa0126ff29a42702",
      "tree": "ddd02bec434e78fa183998b92c4dd8b84bdb8b09",
      "parents": [
        "6469110fac56954861382aa9ae64b5e642d30514"
      ],
      "author": {
        "name": "吴晟 Wu Sheng",
        "email": "wu.sheng@foxmail.com",
        "time": "Fri Feb 13 08:53:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 13 08:53:59 2026 +0800"
      },
      "message": "Introduce OAL V2 engine (#13699)\n\n# OAL V2: A Complete Rewrite of Apache SkyWalking\u0027s Metrics Engine\n\n**TL;DR**: We\u0027ve completely rewritten the OAL (Observability Analysis Language) engine from scratch. The new V2 engine generates bytecode-identical classes to V1, ensuring zero behavioral changes while providing a cleaner architecture, better error messages, and improved maintainability.\n\n## Background: What is OAL?\n\nOAL (Observability Analysis Language) is a domain-specific language that defines how SkyWalking aggregates telemetry data into metrics. Every time you see metrics like `service_resp_time`, `service_cpm`, or `endpoint_sla` in the SkyWalking UI, they\u0027re computed by classes generated from OAL scripts.\n\nHere\u0027s what OAL looks like:\n\n```oal\n// Calculate average response time for services\nservice_resp_time \u003d from(Service.latency).longAvg();\n\n// Calculate success rate (SLA) for endpoints\nendpoint_sla \u003d from(Endpoint.*).filter(status \u003d\u003d true).percent();\n\n// Calculate calls per minute for service relations (client side only)\nservice_relation_client_cpm \u003d from(ServiceRelation.*)\n    .filter(detectPoint \u003d\u003d DetectPoint.CLIENT).cpm();\n```\n\nAt runtime, SkyWalking parses these scripts and generates Java classes using bytecode manipulation (Javassist). These generated classes handle:\n- **Metrics classes**: Data aggregation, serialization, time-bucketing\n- **Builder classes**: Storage conversion (entity ↔ database)\n- **Dispatcher classes**: Routing telemetry data to the correct metrics\n\n## Why Rewrite?\n\nThe original OAL engine (now called V1) was developed in 2018 and has served SkyWalking well. However, over 6+ years of evolution, several architectural issues emerged:\n\n### 1. Mutable Parser Models\n\nV1 uses mutable objects throughout the parsing pipeline. The ANTLR listener directly modifies shared state:\n\n```java\n// V1: Mutable accumulation during parsing\npublic class OALListener {\n    private AnalysisResult current;  // Mutable, reused across rules\n\n    public void enterSource(SourceContext ctx) {\n        current.setSourceName(ctx.getText());  // Direct mutation\n    }\n}\n```\n\nThis made the code harder to reason about and test in isolation.\n\n### 2. Mixed Concerns\n\nV1 combines parsing logic with code generation concerns. The parser directly accesses reflection APIs to validate sources:\n\n```java\n// V1: Parsing and validation coupled together\npublic void exitAggregationStatement(...) {\n    // Parsing + immediate reflection-based validation\n    Class\u003c?\u003e sourceClass \u003d Class.forName(sourceName);\n    Field field \u003d sourceClass.getDeclaredField(fieldName);\n    // ... continues with more interleaved logic\n}\n```\n\n### 3. Limited Error Messages\n\nWhen OAL scripts had syntax errors, V1\u0027s error messages were often cryptic:\n\n```\nOAL parsing failure.\n```\n\nNo line number, no column, no indication of what went wrong.\n\n### 4. String-Based Type Handling\n\nFilter values and function arguments were stored as raw strings, requiring repeated parsing:\n\n```java\n// V1: String-based, parsed multiple times\nString filterValue \u003d \"DetectPoint.CLIENT\";\n// Later: parse again to determine if it\u0027s an enum, string, or number\n```\n\n## What Changed in V2\n\n### Clean Architecture with Immutable Models\n\nV2 introduces a clear separation between parsing, enrichment, and code generation:\n\n```\n┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐\n│  .oal file  │───▶│   Parser    │───▶│  Enricher   │───▶│  Generator  │\n│             │    │             │    │             │    │             │\n│ OAL script  │    │ MetricDef   │    │ CodeGenModel│    │ Bytecode    │\n│             │    │ (immutable) │    │ (metadata)  │    │             │\n└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘\n```\n\nAll parser output is immutable:\n\n```java\n// V2: Immutable model with builder\n@Getter\npublic class MetricDefinition {\n    private final String name;\n    private final SourceReference source;\n    private final List\u003cFilterExpression\u003e filters;  // Unmodifiable\n    private final FunctionCall aggregationFunction;\n\n    // Built via builder, never modified after construction\n}\n```\n\n### Type-Safe Filter Values\n\nV2 represents filter values with proper types from the start:\n\n```java\n// V2: Type-safe from parsing\npublic sealed interface FilterValue {\n    record StringValue(String value) implements FilterValue {}\n    record LongValue(long value) implements FilterValue {}\n    record BooleanValue(boolean value) implements FilterValue {}\n    record EnumValue(String enumClass, String enumValue) implements FilterValue {}\n    record ListValue(List\u003cFilterValue\u003e values) implements FilterValue {}\n}\n```\n\n### Rich Error Messages with Source Location\n\nEvery parsed element carries its source location:\n\n```java\npublic record SourceLocation(String fileName, int line, int column) {\n    public String format() {\n        return fileName + \":\" + line + \":\" + column;\n    }\n}\n```\n\nError messages now include precise locations:\n\n```\nOAL parsing error at core.oal:42:15\n  Undefined source field: Service.latencyy (did you mean: latency?)\n```\n\n### Independent Codebase\n\nV2 has zero dependencies on V1 code. The entire implementation lives in dedicated packages:\n\n```\norg.apache.skywalking.oal.v2/\n├── model/           # Immutable AST models\n├── parser/          # ANTLR listener and parser facade\n├── generator/       # Code generation (Javassist + FreeMarker)\n├── metadata/        # Source introspection utilities\n└── OALEngineV2      # Main entry point\n```\n\n## How We Validated: Bytecode-Level Comparison\n\nThe most critical question: **Does V2 generate identical code to V1?**\n\nWe performed a comprehensive cross-check by:\n\n1. Building both branches (master with V1, oal-v2 branch with V2)\n2. Generating all classes from all 9 OAL scripts\n3. Decompiling 946 generated classes using CFR decompiler\n4. Comparing line-by-line\n\n### Results\n\n| Category | Classes | Identical |\n|----------|---------|-----------|\n| Metrics | 455 | 0* |\n| Builder | 455 | 455 (100%) |\n| Dispatcher | 36 | 36 (100%) |\n| **Total** | **946** | **491** |\n\n*The 455 Metrics classes have exactly **one difference** - a bug fix:\n\n**V1 (incorrect):**\n```java\nif (remoteData.getDataStrings(0) !\u003d \"\") {  // Reference comparison!\n    this.setEntityId(remoteData.getDataStrings(0));\n}\n```\n\n**V2 (correct):**\n```java\nif (!remoteData.getDataStrings(0).isEmpty()) {  // Proper string check\n    this.setEntityId(remoteData.getDataStrings(0));\n}\n```\n\nV1 used `!\u003d` for string comparison, which compares object references rather than string content. V2 fixes this by using `.isEmpty()`. This is the **only behavioral difference**, and it\u0027s a bug fix.\n\n## Migration Strategy\n\n### For Users: Zero Action Required\n\nOAL V2 is a pure internal refactoring. Your OAL scripts work exactly as before. No configuration changes, no migration steps.\n\n### For Contributors: Simplified Development\n\nIf you\u0027re extending SkyWalking\u0027s metrics:\n\n1. **Same OAL syntax** - All existing scripts work unchanged\n2. **Better debugging** - Set `SW_OAL_ENGINE_DEBUG\u003dtrue` to write generated `.class` files to disk\n3. **Clearer code** - Each component has a single responsibility\n4. **Easier testing** - Immutable models can be constructed directly in tests\n\n### For Maintainers: Cleaner Codebase\n\nThe V1 code remains in the repository temporarily for reference but is no longer used. Future OAL enhancements (new aggregation functions, new filter operators, etc.) will be implemented in V2 only.\n\n## Technical Deep Dive\n\n### Parser Implementation\n\nV2 uses the same ANTLR4 grammar as V1 (`OALParser.g4`, `OALLexer.g4`) but with a completely new listener:\n\n```java\npublic class OALListenerV2 extends OALParserBaseListener {\n    private final List\u003cMetricDefinition\u003e metrics \u003d new ArrayList\u003c\u003e();\n    private MetricDefinition.Builder currentBuilder;\n\n    @Override\n    public void enterMetricStatement(MetricStatementContext ctx) {\n        // Start fresh builder for each metric\n        currentBuilder \u003d MetricDefinition.builder()\n            .location(locationOf(ctx));\n    }\n\n    @Override\n    public void exitMetricStatement(MetricStatementContext ctx) {\n        // Finalize and collect\n        metrics.add(currentBuilder.build());\n        currentBuilder \u003d null;\n    }\n}\n```\n\n### Enrichment Phase\n\nThe `MetricDefinitionEnricher` adds metadata required for code generation:\n\n```java\npublic class MetricDefinitionEnricher {\n    public CodeGenModel enrich(MetricDefinition metric) {\n        // Resolve source class via reflection\n        Class\u003c?\u003e sourceClass \u003d resolveSourceClass(metric.getSource());\n\n        // Extract column metadata from annotations\n        List\u003cSourceColumn\u003e columns \u003d extractColumns(sourceClass);\n\n        // Determine persistent fields based on aggregation function\n        List\u003cPersistentField\u003e fields \u003d determinePersistentFields(\n            metric.getAggregationFunction(), columns);\n\n        return CodeGenModel.builder()\n            .metric(metric)\n            .sourceColumns(columns)\n            .persistentFields(fields)\n            .build();\n    }\n}\n```\n\n### Code Generation\n\nV2 uses FreeMarker templates for generating method bodies:\n\n```ftl\n\u003c#-- metrics/deserialize.ftl --\u003e\n@Override\npublic void deserialize(RemoteData remoteData) {\n    \u003c#list serializeFields.stringFields as field\u003e\n    if (!remoteData.getDataStrings(${field?index}).isEmpty()) {\n        this.${field.setter}(remoteData.getDataStrings(${field?index}));\n    }\n    \u003c/#list\u003e\n    // ... more field types\n}\n```\n\nThis is cleaner than V1\u0027s string concatenation approach and easier to maintain.\n\n## Future Possibilities\n\nWith V2\u0027s clean architecture, several enhancements become easier:\n\n1. **Static analysis** - Detect issues before runtime (unused metrics, type mismatches)\n2. **Incremental compilation** - Only regenerate changed metrics\n3. **New aggregation functions** - Cleaner extension points\n\n## Conclusion\n\nOAL V2 is a foundational improvement to SkyWalking\u0027s core metrics engine. While invisible to end users, it sets the stage for easier maintenance and future enhancements. The bytecode-level validation gives us confidence that this rewrite preserves exact behavioral compatibility (minus one bug fix).\n"
    },
    {
      "commit": "6469110fac56954861382aa9ae64b5e642d30514",
      "tree": "2e418ce1e14d8ecce27a5f40b214eb81f32c83ae",
      "parents": [
        "233f5ef1430443dafe10477e701b00b7f0843c12"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Tue Feb 10 18:43:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Feb 10 18:43:05 2026 +0800"
      },
      "message": "Envoy metrics service receiver: support config MAL rules files. (#13697)\n\n"
    },
    {
      "commit": "233f5ef1430443dafe10477e701b00b7f0843c12",
      "tree": "ac3eae707961fb87886905c75606f95cda696f0f",
      "parents": [
        "e6bbebe107096bf50ed79e40a88f230bd1237c3d"
      ],
      "author": {
        "name": "Fine0830",
        "email": "fanxue0830@gmail.com",
        "time": "Tue Feb 10 15:41:27 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Feb 10 15:41:27 2026 +0800"
      },
      "message": "Sync UI (#13695)\n\n"
    },
    {
      "commit": "e6bbebe107096bf50ed79e40a88f230bd1237c3d",
      "tree": "ffb0a549b2d9ed5bc8c523483b240a23dd516247",
      "parents": [
        "84e45c5677a75cd0efcd8e60e254da057f828c64"
      ],
      "author": {
        "name": "Wan Kai",
        "email": "wankai123@foxmail.com",
        "time": "Fri Feb 06 12:35:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 06 17:35:28 2026 +1300"
      },
      "message": "Sync UI (#13693)\n\n"
    }
  ],
  "next": "84e45c5677a75cd0efcd8e60e254da057f828c64"
}
