Skip to content

feat(semantic-layers): add an SDK conformance kit for providers - #45395

Merged
aminghadersohi merged 14 commits into
apache:masterfrom
mikebridge:sc-127359-sdk-conformance-kit
Oct 28, 2026
Merged

aminghadersohi merged 14 commits into
apache:masterfrom
mikebridge:sc-127359-sdk-conformance-kit

Conversation

@mikebridge

@mikebridge mikebridge commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

SUMMARY

TL;DR: Provider authors get an opt-in pytest conformance suite that runs without a Superset application and checks observable results and failure categories.

Background: The semantic-layer SDK defines common query and metadata contracts, while each provider owns construction, transport and backend fixtures.

Problem: A provider can implement the right methods yet return the wrong grain, filter result, ordering or error category. Shared behavioral checks make those regressions easier to detect before integration.

What this changes:

  • Add SemanticProviderConformance, with provider-owned fixtures for deterministic queries, controlled errors and catalog changes.
  • Check selection identity, grains, NULL/OR filtering, explicit ordering before limits, advertised capabilities, refusal/fault/completeness boundaries, metadata generations and unbound discovery.
  • Check provider imports and static configuration schemas in a fresh interpreter, rejecting host imports and common schema I/O operations.
  • Run the suite against an SDK-only SQLite example and document how providers supply independent expected results.

Depends on #45393. The conformance layer diff is available in the fork comparison.

The suite is optional test support; importing the SDK normally does not load pytest. Factory signatures, context types, production transports, host execution, and count/value success conventions are unchanged. Default chronological ordering remains a separate change in #45389. Provider integration lanes must use their pinned SDK and retain host and live-provider checks.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not applicable: SDK test support and documentation only.

TESTING INSTRUCTIONS

With the SDK dependencies and pytest installed, run from the repository root:

PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTHONPATH=$PWD/superset-core/src python -m pytest -q -o addopts='' superset-core/tests/semantic_layers/test_conformance.py superset-core/tests/semantic_layers/test_registration.py superset-core/tests/semantic_layers/test_view.py

The example uses in-process SQLite, no server or credentials. Expected Arrow tables are independently specified. Controlled mutations were detected for NULL/OR behavior, grain handling, ordering, partial success, fault classification, stale catalogs and token relabeling. Negative import/schema tests also verify that catching a tripwire exception cannot make the check pass.

The import/schema tripwires are not a sandbox or exhaustive proof against native-driver I/O. Provider lanes must also poison their own transport and credential entry points. Passing these checks does not establish deployed host or live-warehouse conformance.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@netlify

netlify Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 36f13eb
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ae19131c09db752e3f1640f
😎 Deploy Preview https://deploy-preview-45395--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the doc Namespace | Anything related to documentation label Oct 16, 2026
@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.58%. Comparing base (1aceca3) to head (36f13eb).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #45395      +/-   ##
==========================================
+ Coverage   84.57%   84.58%   +0.01%     
==========================================
  Files        3069     3073       +4     
  Lines      205227   205734     +507     
  Branches    48222    48180      -42     
==========================================
+ Hits       173561   174028     +467     
- Misses      28362    28402      +40     
  Partials     3304     3304              
Flag Coverage Δ
hive 34.56% <ø> (ø)
mysql 52.51% <ø> (ø)
postgres 52.51% <ø> (ø)
presto 36.28% <ø> (ø)
python 87.65% <ø> (ø)
sqlite 52.28% <ø> (ø)
superset-extensions-cli 91.24% <ø> (?)
unit 82.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 19, 2026
@github-actions github-actions Bot added github_actions Pull requests that update GitHub Actions code risk:ci-script PR modifies scripts that execute in CI (supply chain risk) and removed requires:rebase Requires rebasing on top of current master labels Oct 20, 2026
…rmance-kit

# Conflicts:
#	docs/developer_docs/extensions/contribution-types.md
#	superset-core/src/superset_core/semantic_layers/testing.py
#	superset-core/tests/semantic_layers/example_provider.py
#	superset-core/tests/semantic_layers/test_registration.py
@github-actions github-actions Bot removed github_actions Pull requests that update GitHub Actions code risk:ci-script PR modifies scripts that execute in CI (supply chain risk) labels Oct 20, 2026
@mikebridge
mikebridge marked this pull request as ready for review October 20, 2026 19:30
Comment on lines +123 to +124
if case == "grain":
assert any(dimension.grain is not None for dimension in query.dimensions)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This accepts a single grained dimension and never checks another variant of the same name, so incorrect grain-variant selection can pass.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Incomplete implementation

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-core/src/superset_core/semantic_layers/testing.py
**Line:** 123:124
**Comment:**
	*Incomplete Implementation: This accepts a single grained dimension and never checks another variant of the same name, so incorrect grain-variant selection can pass.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I don't think this is a real gap. The grain case doesn't stop at checking that the query has a grained dimension: the test then runs the query and compares the provider's result with the fixture's expected Arrow table, including column names and row order. If a provider picks the wrong grain variant, the bucketed rows differ and the comparison fails.

Requiring a second variant of the same dimension in the catalog would also wrongly fail providers that only expose one grain. Making the expected table actually tell grains apart is the fixture author's job, just as the existing num_rows > 0 guard covers the no-data case. Leaving this as is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Customized review instruction saved!

Instruction:

For semantic-query grain tests, verify grain selection by comparing the query result with an expected fixture that distinguishes the grain; do not require every provider to expose multiple grain variants.

Applied to:

  • **/test/**
  • **/tests/**
  • **/*test*.py

💡 To manage or update this instruction, visit: CodeAnt AI Settings

@codeant-ai-for-open-source

codeant-ai-for-open-source Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

CodeAnt  CodeAnt PR Risk

🟢  Low Risk

  • The PR appears safe to merge; the grain test verifies executed results against an expected table that distinguishes the selected grain.

Assessed commit: 36f13eb7fc8a

@fitzee fitzee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: no blocking issues; several non-blocking gaps where the kit is stricter or looser than the host contract.

Ran superset-core/tests on the PR head (48 passed; CI green). I also ran the example suite against provider mutations:

Mutation Result
truncate-then-sort caught (order_limit)
fault reported as refusal caught (3 fault cases)
stale catalog on unbound construction caught
get_values returns {x, y} columns and ignores filters passes
get_row_count returns {"nonsense": ["x"]} passes
fault path raises TypeError (provider bug) passes
result columns metrics-first fails 7 cases
null-free columns marked nullable=False fails 7 cases

The last two show the kit is stricter than the host (inline). The get_values/get_row_count rows show it is looser. The doc's "count lane only checks failure propagation" disclaimer covers those two, but the host already relies on a fixed get_values shape. SemanticView.values_for_column in superset/semantic_layers/models.py reads the column named dimension.name (or a single column) and raises ValueError otherwise. A success case for that would catch a broken value-suggestion path that currently passes.

Other notes:

  • Non-blocking. The documented opt-in (pytest_plugins plus from superset_core.semantic_layers.testing import SemanticProviderConformance in the same test module) imports the plugin before pytest can rewrite it. That emits PytestAssertRewriteWarning: Module already imported so cannot be rewritten, and the bare asserts in the kit then fail with a plain AssertionError and no detail (for example, the fault lane shows nothing about which exception was raised). preset-io/superset-shell#5607 loads it with -p, so it gets rewriting, and its known_failures regexes depend on the rewritten text. Either recommend -p superset_core.semantic_layers.testing or a root conftest.py in the doc, or call pytest.register_assert_rewrite / give every assert an explicit message.
  • Nit. The doc says "Include at least two grains for one name", but the kit only checks grain is not None (and the reply to the earlier bot comment argues one grain should be allowed). Align the doc with what is enforced.
  • Nit. _check_isolation rejects flask/flask_appbuilder, which are hard dependencies of apache-superset-core. A provider importing an SDK module such as superset_core.common.models fails with "Provider imported host module flask_appbuilder". This is probably intended, but the message blames the provider for an SDK import.

assert full.results.num_rows > query.limit, "seed more rows than the limit"
assert full.results.slice(query.offset or 0, query.limit).equals(expected)
result: SemanticResult = semantic_view.get_table(query)
assert result.results.equals(expected), (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking (stricter than the contract). pa.Table.equals compares column order and field nullability. The SDK only promises "one column per selected member name" (types.py, view.py), and the host selects by name (cache_transform.py: results.select(columns)). A provider that returns metrics before dimensions, or marks null-free columns nullable=False, fails all 6 query cases and the capability case even though the host accepts it.

The nullability failure is also hard to diagnose because the message prints identical data:

AssertionError: identity: expected {'category': [None, 'A', 'B'], 'total': [3, 7, 8]}, got {'category': [None, 'A', 'B'], 'total': [3, 7, 8]}

Suggest comparing result.results.select(expected.column_names) after normalizing nullability (or comparing types and values per column), and including schema in the failure message. The same applies to line 207.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0cb0130. The kit compares typed columns by name, so field order and nullability no longer reject equivalent results. This applies to the query, order-before-limit and capability checks. The regressions cover reordered and nonnullable columns; wrong types, values, row order and duplicate names still fail, with schema details in the diagnostic.

One follow-up hardening item: the negative comparison regressions should match the specific assertion diagnostic, so a future fixture change cannot make an unrelated assertion satisfy the test.

assert isinstance(error.value, SemanticResultCompletenessError)
assert error.value.reason == kind
else:
assert not isinstance(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking (vacuous). The fault lane passes on any exception that is not an SDK refusal or completeness error, including TypeError, AttributeError, KeyError or AssertionError from a provider bug or a broken fixture. With the example's fault path changed to raise TypeError("'NoneType' object is not subscriptable"), all fault cases pass. Consider letting the fixture declare the expected fault type, for example {"get_table:fault": (callable, ConnectionError)}, or at least rejecting TypeError, AttributeError, NameError and AssertionError here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0cb0130. The fault lane rejects TypeError, AttributeError, NameError, AssertionError and KeyError, in addition to refusal/completeness exceptions. Each programming-error variant has a regression. Backend-specific exception classes remain provider-owned, and the guide calls for provider lanes to assert their exact backend fault type too.

This implements the minimum guard you suggested; it is still a heuristic, since other programming errors can pass. A fixture-declared expected fault type is the stronger follow-up, alongside the deferred value/count success cases.

assert query.offset > 0
assert query.limit is not None
assert query.limit > 0
elif name in {"GROUP_LIMIT", "GROUP_OTHERS"}:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking (looser). Only ROW_OFFSET and GROUP_* witnesses are checked for actually using the feature. For ADHOC_COLUMN_EXPRESSIONS and ADHOC_EXPRESSIONS_IN_ORDERBY, any plain query satisfies the lane, so "every advertised feature has a result-level witness" does not hold for them. An AdhocExpression should be required in dimensions or order respectively.

Also, a single test loops over every feature, so the first failing feature hides the others. Parametrizing over SemanticViewFeature, with a skip when the feature is not advertised, would report each one separately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0cb0130. Each SDK capability is a separate parametrized case, with an explicit skip when unadvertised. The two ad-hoc cases require an AdhocExpression in the selected dimensions or order keys respectively; plain-query witnesses fail before provider execution.

# A captured view must never claim a later discovery token.
assert first.metadata_cache_token in {None, "generation-a"}
semantic_change_catalog()
unbound: SemanticView = semantic_make_view(None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking (stricter than the contract). No host path constructs a provider without a token. SemanticLayer.from_configuration takes cache_token: str (required, not optional), and its only host caller (execution.py, the bind method) always passes layer.metadata_generation. The "unbound" obligation exists only in this kit and in the sentence this PR adds to semantic-layer-cache-clear.md.

As a result, adapters have to invent a tokenless path. preset-io/superset-shell#5607 already calls from_configuration(..., cache_token=None) against the str annotation. Either drop the None leg (keep the two-generation freshness and no-relabel checks), or make the SDK signature str | None and document when the host uses it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed; fixed in 0cb0130 by dropping the unsupported None-construction leg and its cache-guide sentence. The kit passes string tokens matching from_configuration and the host binding path. It still verifies catalog freshness across two generations and that an older captured view is never relabeled. This does not change the SDK factory signature or legacy metadata_cache_token=None echoes. The shell adapter should use that required string-token path rather than widen the SDK contract.

)
assert completed.returncode == 0, completed.stdout + completed.stderr

# TODO: add the default chronological-order case after its SDK rule lands.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. This TODO is stale. #45389 landed the rule, and it is host-side: with_default_order fills order before dispatch, and the SemanticQuery docstring says a get_table call "receives an empty order only when no temporal dimension is selected". The provider-side obligation is just "honour the explicit order", which order_limit/grain already cover. The same applies to the "Default chronological ordering will be added after that SDK rule lands" sentence in semantic-layer-provider-testing.md.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0cb0130. Removed the TODO and updated the guide: the host supplies the default chronological order, while the provider must honor the explicit order it receives before limit/offset. The existing grain and order-limit witnesses cover that obligation.

I also addressed the documentation notes from your review body: the plugin-loading example uses -p before collection, the grain wording matches the enforced witness, and the isolation diagnostic identifies transitive framework imports. The value/count success gaps are real, including the host's existing get_values shape expectation. I suggest a separate follow-up for success fixtures and filtered-value assertions; this patch keeps those methods scoped to error propagation. The SDK suite passes with 64 tests and four skips for unadvertised capabilities.

Comment thread superset-core/src/superset_core/semantic_layers/testing.py Outdated
Comment thread superset-core/src/superset_core/semantic_layers/testing.py Outdated
Comment thread superset-core/tests/semantic_layers/test_conformance.py Outdated

@fitzee fitzee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things to fix before merge. The grouped-OR/NULL case does not use the filter shape the host actually sends, and the ADHOC_COLUMN_EXPRESSIONS lane cannot be met without breaking the SDK's own types. Both are part of the fixture contract that providers will code against, so fix them before this ships as public SDK surface.

I ran the suite on 0cb0130 (43 passed, 4 skipped). Then I mutated conformance_provider.py one change at a time and re-ran TestExampleConformance:

Mutation Result
ignore offset caught
ignore grain (group by raw day) caught
drop the parentheses around the OR group passes
drop every top-level comparison filter (=, >=, <, IN, ...); keep only IS [NOT] NULL and OR groups passes
return a column-less pa.table({}) when no rows match passes
return an empty catalog for generation B passes
metadata_cache_token always None passes

Packaging is fine. testing.py is the only module that imports pytest, nothing in superset_core.semantic_layers imports it, and [testing] is the extra.

Blocking

  1. The OR case doesn't test the shape the host sends (testing.py:134-135, example fixture test_conformance.py:123-133).
    • The lane accepts any OrFilter, and the example uses {IS_NULL, EQUALS "A"}.
    • The host never builds that group. _normalize_membership_filter in superset/semantic_layers/mapper.py turns a mixed selection IN ("A", NULL) into OrFilter({IN frozenset({"A"}), IS_NULL}). That is the path the Operator docstring says "providers must render both branches of".
    • So a provider can mishandle IN inside the group and still pass. Examples: binding the frozenset as a single parameter, rendering only one member, or rejecting IN outright. The example provider rejects IN with INVALID_FILTER (conformance_provider.py:198-201) and passes the whole suite.
    • Precedence also goes unchecked, as noted in the existing thread. I confirmed it: dropping the parentheses passes.
    • Fix: require an IN leaf with at least two non-NULL values in the group, plus an outer AND predicate whose result differs when the parentheses are lost. The doc currently only asks for the outer AND "when the provider supports" it (semantic-layer-provider-testing.md:65-66); make the kit enforce it.
  2. ADHOC_COLUMN_EXPRESSIONS can't be met (testing.py:214-217).
    • SemanticQuery.dimensions is list[Dimension].
    • The host rejects ad-hoc dimensions: mapper.py raises "Adhoc dimensions are not supported in Semantic Views."
    • A provider advertising this feature can only pass by building a query that violates the SDK type. Fixing that later would change public fixture expectations.
    • Fix: skip this feature with an explicit "no SDK query shape yet" reason until the SDK defines one. Keep the ADHOC_EXPRESSIONS_IN_ORDERBY check, which matches the host's _get_order_from_query_object.

Non-blocking

  • No time-range or plain comparison filter case (testing.py:103-105). Every time-series chart sends GREATER_THAN_OR_EQUAL/LESS_THAN on the temporal column (mapper.py, time-range filters). Every filter-box selection sends IN. A provider that silently drops both passes (row 4 above). This list of case keys is effectively the fixture API, and adding required keys later breaks every subclass. Consider adding in and time_range cases now.
  • Empty results are never checked (testing.py:116-118). Requiring nonempty expected tables is right for the discriminating cases. But nothing checks that a query matching no rows still returns one typed column per selected member, which get_table promises ("Name each result column after the member that produced it"). The host relies on this when it selects columns by name from cached results (cache_transform.py:249). A filter matching nothing would cover it.
  • The metadata-generation check is weaker than its docstring (testing.py:236-243).
    • The existing thread already covers after != before accepting an empty generation-B catalog. I confirmed that it passes.
    • Separately, a provider whose metadata_cache_token is always None satisfies all three token assertions, so "never relabeled" is vacuous for it.
    • If None stays allowed for legacy views, say in the doc that the relabel check doesn't apply to them. Alternatively, add a fixture flag so a cached provider opts into strict token echo.
  • A missing required case gives a bare KeyError (testing.py:115, :163, :197). For example, a provider with no MONTH grain gets KeyError: 'grain'. pytest.fail(f"semantic_query_cases is missing required case {case!r}") would tell the author which fixture key is missing.
  • The override recipe for unsupported error cases is heavier than needed (semantic-layer-provider-testing.md:87-93). The operation runs inside pytest.raises(Exception), and pytest.skip raises a BaseException. So calling pytest.skip(reason) from the fixture callable already skips just that case, without overriding the method or repeating both parametrize decorators. Documenting that would be simpler and harder to get wrong.
  • API stability. SemanticProviderConformance, its fixture names and their tuple and mapping shapes are now public apache-superset-core (0.1.0) API, and external providers subclass them. Add one sentence to the guide saying how new cases and fixtures will be introduced. For example: new required fixtures only in a minor version bump, with the case list in the changelog. Then provider lanes that pin the SDK know what an upgrade costs.
  • Nit. pytest is unpinned in [testing]. The kit uses pytest.MonkeyPatch.context(), which needs pytest ≥ 6.2. A lower bound would avoid confusing failures on old environments.

@mikebridge

Copy link
Copy Markdown
Contributor Author

Following up on your review:

Addressed the two pre-merge fixes in 06568a7. The OR witness uses the host-normalized IN/IS_NULL shape with two non-NULL membership values and an outer comparison. The example data exposes lost parentheses in either filter order, dropped top-level comparisons, and a dropped membership value. Those mutations have regressions against the real SQLite example.

ADHOC_COLUMN_EXPRESSIONS skips with an explicit “no SDK query shape yet” reason; no query violating list[Dimension] is required. The representable ad-hoc ordering check stays in place. The generation check also requires an additive catalog, and the guide makes the None-token limitation explicit. The SDK suite passes with 77 tests and four explicit skips.

I kept the existing required case keys for this patch. Dedicated IN/time-range/empty-result cases and the fixture-versioning policy need to be agreed together before adding required fixtures. The missing-case diagnostics, simpler skip recipe and pytest lower bound remain non-blocking follow-ups; the empty-result gap is not claimed fixed here.

Comment thread superset-core/tests/semantic_layers/conformance_provider.py
Comment thread superset-core/src/superset_core/semantic_layers/testing.py
@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 22, 2026
@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Oct 22, 2026

@aminghadersohi aminghadersohi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. All prior threads are resolved in code (order/nullability, fault-lane programming errors, ad-hoc witnesses, generation-change catalog check, grouped-OR seed, grain skip). CI is green. Remaining non-blocking suggestions from fitzee's review (in/time_range cases, empty-result check, clearer missing-case failure, pytest lower bound) can be follow-ups.

@sadpandajoe sadpandajoe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@aminghadersohi
aminghadersohi merged commit 83faf45 into apache:master Oct 28, 2026
74 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc Namespace | Anything related to documentation size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants