Skip to content

feat(ci): restore workflows and label PPL linter CI - #8

Closed
Hanyu-W wants to merge 78 commits into
codex/ppl-lint-13-rule-ci-implementationfrom
codex/ppl-linter-multi-surface-restored
Closed

feat(ci): restore workflows and label PPL linter CI#8
Hanyu-W wants to merge 78 commits into
codex/ppl-lint-13-rule-ci-implementationfrom
codex/ppl-linter-multi-surface-restored

Conversation

@Hanyu-W

@Hanyu-W Hanyu-W commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Description

Clean replacement for #7. Builds on #5 and includes the latest multi-surface PPL lint compatibility CI.

  • Preserves every workflow from the base, including the upstream-owned Analytics Engine Compatibility workflow, SQL Java CI, and Security Plugin IT.
  • Clearly labels the two PPL lint workflows as [Linter] PPL rule validation and [Linter] PPL multi-surface compatibility.
  • Plans exactly three compatibility configurations: OpenSearch 2.19.6 with OSD's compiled-simplified grammar, the highest eligible official GA release with its runtime bundle, and the SQL PR build with its runtime bundle.
  • Emits a complete schema-v3 12 x 3 compatibility report while preserving missing legs as inconclusive columns and publishing artifacts before enforcement.
  • Applies surface, version, then engine-mode exclusions before detector execution.
  • Distinguishes detector regression, full engine relaxation, partial engine relaxation, and contract drift with query-level evidence and remediation.

Testing

  • node --test scripts/ppl-lint/__tests__/*.test.mjs (218 passing)
  • actionlint .github/workflows/ppl-lint-multiversion-validation.yml .github/workflows/ppl-lint-rule-validation.yml
  • node --check scripts/ppl-lint/aggregate-compatibility.mjs
  • node --check scripts/ppl-lint/plan-compatibility.mjs
  • node --check scripts/ppl-lint/run-frontend-contract.mjs
  • git diff --check
  • Verified all inherited workflow files are unchanged from the feat(ci): enforce exact PPL lint frontend contracts #5 base.

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • Commits are signed per the DCO using --signoff.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

noCharger and others added 20 commits July 17, 2026 13:12
This reverts the changes introduced by PR opensearch-project#5599 (merge commit
454ac4e).
* [Feature] Add PPL makeresults command

Add the makeresults leading command on the Calcite (v3) path. It generates
in-memory rows with no index scan:

- count=N produces N rows, each with a single _time timestamp set to query time
- format=csv|json data=... parses an inline literal into typed rows, with column
  types synthesized following OpenSearch dynamic-mapping semantics and surfaced
  through the same type path an index scan uses (JSON int->long, decimal->float,
  string->keyword; typed CSV name:type via cast's vocabulary; bare CSV->string)

Grammar lives in the ppl/ copies only. Adds the MakeResults AST node, AstBuilder
wiring with MakeResultsDataParser, CalciteRelNodeVisitor.visitMakeResults building
LogicalValues + Project, a V2 Analyzer reject stub, and anonymizer rendering.
Includes unit tests, AstBuilder and anonymizer tests, integration tests, and the
user doc.

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>

* Address Songkan's comments

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>

* Address PR-Agent edge cases: empty-first-row guard, skip whitespace CSV lines

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>

* makeresults: cap inline data by cells (rows x columns) and per-value width

Replace the flat data<=5000 rows guard with rows*cols<=5000 cells (the
Janino 64KB per-method codegen cliff scales with rows x columns, so a flat
row cap was unsafe for multi-column data). Replace the 29999 total-data
char cap with a per-value guard (single cell value <= 60000 chars, under
the JVM 65535-byte constant-pool limit). Trim over-verbose comments.

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>

* makeresults: add ragged-row and at-limit boundary tests; fix index version to 3.8

Add four boundary tests to CalcitePPLMakeResultsTest: CSV row with more
columns rejected, CSV row with fewer columns padded to null, count at the
5000 cap allowed, and a cell value at the 60000-char limit allowed. Correct
the PPL command index to list makeresults as 3.8 (since 3.8).

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>

---------

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
Signed-off-by: Songkan Tang <songkant@amazon.com>
* Support constant_keyword field type in PPL

Register `constant_keyword` in the OpenSearch mapping parser so PPL
recognises the type and treats it as a string, matching the semantics
of the OpenSearch `constant_keyword` field type (a single value shared
by every document in an index).

Resolves opensearch-project#3703

Signed-off-by: Peng Huo <penghuo@gmail.com>

* Document constant_keyword in PPL data types table

Signed-off-by: Peng Huo <penghuo@gmail.com>

* Index tenant value explicitly in constant_keyword integ test

The v2 engine reads field values from _source. OpenSearch does not
back-fill the mapping's constant value into _source when a document
omits the field, so the row came back with tenant=null on the v2 path
even though the mapping declares the value.

Index the tenant field in the document so both v2 and Calcite paths
return the same value.

Signed-off-by: Peng Huo <penghuo@gmail.com>

---------

Signed-off-by: Peng Huo <penghuo@gmail.com>
* xy series implmentation

Signed-off-by: Asif Bashar <abashar@apple.com>

* xy series implmentation

Signed-off-by: Asif Bashar <abashar@apple.com>

* xy series implmentation

Signed-off-by: Asif Bashar <abashar@apple.com>

* xy series implmentation

Signed-off-by: Asif Bashar <abashar@apple.com>

* xy series implmentation

Signed-off-by: Asif Bashar <abashar@apple.com>

* index.md updated

Signed-off-by: Asif Bashar <abashar@apple.com>

* removed duplicate format added during merging

Signed-off-by: Asif Bashar <abashar@apple.com>

* fix compile issue

Signed-off-by: Asif Bashar <abashar@apple.com>

* fix test failure

Signed-off-by: Asif Bashar <abashar@apple.com>

* added missing explain expection output files missed during merge conflict.

Signed-off-by: Asif Bashar <abashar@apple.com>

* removed extra formatting changes

Signed-off-by: Asif Bashar <abashar@apple.com>

* removed extra formatting changes

Signed-off-by: Asif Bashar <abashar@apple.com>

* fix explain test failure

Signed-off-by: Asif Bashar <abashar@apple.com>

---------

Signed-off-by: Asif Bashar <abashar@apple.com>
* Decouple Calcite PPL planning from ExprType

Rewrite the Calcite-side PPL planning surface so RelNode/RexNode code,
coercion, type checking, and UDF implementations operate on RelDataType
instead of bouncing through the v2 ExprType system.

UDT identity at planning time:
- UDTs (ExprDateType, ExprTimeType, ExprTimeStampType, ExprIPType,
  ExprBinaryType) are recognised via instanceof rather than getExprType().
  Subclasses are preserved through createTypeWithNullability /
  createTypeWithCharsetAndCollation via a cloneWith hook on
  ExprSqlType / ExprJavaType so the instanceof checks survive type-factory
  operations.

Calcite-side rewrites:
- CoercionUtils: new RelDataType-typed common-type resolver with an
  internal CoercionTag widening DAG that mirrors v2 semantics.
  Widening produces UDT-normalized temporal types and always makes the
  cast target nullable so safe cast + primitive aggregators do not NPE
  on parse failure.
- PPLTypeChecker: signatures expressed as List<List<RelDataType>>; adds
  renderTypeName() for error messages; folds DECIMAL to DOUBLE.
  isComparable() accepts UDT temporal vs standard temporal of same kind.
- PPLOperandTypes: exposes RelDataType signature constants
  (DATE_UDT, INTEGER_T, ...).
- PPLFuncImpTable.requiresNumericArgument: uses SqlTypeUtil.isNumeric
  and SqlTypeName.ANY for the "unknown-type" check.
- ExtendedRexBuilder, AddSubDate/Extract/Format/LastDay/PeriodName/
  TimestampAdd/TimestampDiff/Weekday/Span/WidthBucket and the ip UDFs
  branch on UDT classes and pass RelDataType through their implementors.
- visitCast in CalciteRexNodeVisitor maps AST DataType directly to
  RelDataType, removing the DataType.getCoreType() round-trip.
- CurrentFunction / FormatFunction take an internal Kind / boolean
  discriminator rather than an ExprCoreType.
- DatetimeExtension switches from ExprUDT enum to type-class checks.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* Fix PPLTypeChecker.typesMatch UDT comparison and revert CalciteRexNodeVisitor churn

typesMatch compared UDTs via getClass(), but addCharsetAndCollation strips
concrete subclass identity from VARCHAR-backed UDTs (ExprDateType,
ExprTimeType, ExprTimeStampType, ExprBinaryType) — they all collapse to
ExprSqlType. That let wrapUDT accept mismatched UDTs at the signature
gate: `cidrmatch(date_field, "1.2.3.4/24")` matched the [BINARY_UDT,
STRING_T] signature and crashed at runtime trying to parse a date as an
IP. Compare via the ExprUDT tag instead, matching what
PPLComparableTypeChecker.isComparable already does.

Also revert CalciteRexNodeVisitor to upstream/main — the earlier
inlined visitCast switch duplicated convertExprTypeToRelDataType, and
the visitBetween comment tweak had no code change.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* spotless: reflow typesMatch javadoc

Signed-off-by: Peng Huo <penghuo@gmail.com>

* CoercionUtils: convert RelDataType→ExprType at boundary, reuse main-branch lattice

The parallel CoercionTag lattice duplicated ExprCoreType.getParent()
(BYTE→SHORT→...→DOUBLE; STRING→DATE/TIME/TIMESTAMP/BOOLEAN/IP;
DATE/TIME→TIMESTAMP), and adding STRING→TIMESTAMP as a widening edge to
make ranking tie with STRING→DOUBLE conflated widening truth with
signature preference. Also, normalizeTemporalToUdt was a downstream
fixup for plain-Calcite TIMESTAMP results that only handled bare
TIMESTAMP/DATE/TIME and missed the TZ variants, and the public
hasString(List<RexNode>) used SqlTypeUtil.isCharacter which incorrectly
classified VARCHAR-backed UDTs (DATE/TIME/TIMESTAMP/BINARY) as STRING.

Replace all of it with a boundary conversion:
convertRelDataTypeToExprType at the entry, run the widening + rule set
exactly as upstream/main does over ExprCoreType, and round-trip results
through convertExprTypeToRelDataType — which already returns the UDT
variant for temporals and IP, so the "normalize to UDT" step happens for
free. TZ variants are handled correctly (convertSqlTypeNameToExprType
folds them into TIMESTAMP/TIME). The hasString public API now matches
the private one (both check ExprCoreType.STRING). Deletes ~90 lines of
duplicated lattice.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* CoercionUtils: restore upstream body verbatim, keep only boundary adapter

Reduce this file to its minimal delta vs main: the only necessary change
is that PPLTypeChecker.getParameterTypes() on this branch returns
List<List<RelDataType>> (upstream returns List<List<ExprType>>). Adapt
at the entry of castArguments; everything else is main-branch code.

Delete the parallel CoercionTag lattice, PARENTS map, normalizeTemporalToUdt,
and the RelDataType-flavored resolveCommonType / max / distance helpers
introduced in the earlier version of this file. The upstream ExprType-based
lattice is the source of truth for widening, and the RelDataType→ExprType
conversion via convertRelDataTypeToExprType at the public boundary handles
UDTs uniformly.

Test: StubTypeChecker.getParameterTypes now returns List<List<RelDataType>>
to match the branch's PPLTypeChecker interface. Substitute
SqlTypeName.GEOMETRY for ExprCoreType.GEO_POINT in the
no-compatible-signature test since GEO_POINT isn't in
convertExprTypeToRelDataType's switch.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* PPLTypeChecker.isComparable: match upstream semantics, add unit tests

Replace the custom "same UDT kind → temporal kind → same SqlTypeFamily"
branches with the upstream approach: convert both sides to ExprType via
convertRelDataTypeToExprType and use ExprType.shouldCast to decide
comparability. Two regressions surface without this:

- plain VARBINARY vs EXPR_BINARY UDT (both map to ExprCoreType.BINARY)
  was rejected because the family-match guard excluded UDTs, so the
  branch never triggered on a UDT-plain pair.
- day-time interval vs year-month interval (both map to INTERVAL) was
  rejected because Calcite splits their SqlTypeFamily into
  INTERVAL_DAY_TIME / INTERVAL_YEAR_MONTH.

Both compare equal under shouldCast so upstream semantics preserves them.

The unused temporalKind helper is removed; new
PPLComparableTypeCheckerTest covers numeric, same-UDT, plain-vs-UDT
temporal/binary, cross-UDT rejection, interval mixing, ANY fallback,
struct field-by-field, and the IP-outer-checker rejection.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* PPLTypeChecker.isComparable: revert to upstream, exercise via public checker

Restore isComparable and its javadoc byte-for-byte to upstream/main —
the previous adaptation added no behavior and the private visibility is
correct. Drive the tests through the public
PPLComparableTypeChecker.checkOperandTypes entry point instead of
lifting isComparable to package-private just for tests.

Signed-off-by: Peng Huo <penghuo@gmail.com>

* PPLComparableTypeCheckerTest: reword regression comments as guardrails

These pairs don't fix a shipped bug — the upstream isComparable already
handles them. Reword the comments to reflect that the tests exist to
guard against future edits that would classify via SqlTypeFamily or Java
class, either of which would break these specific pairs.

Signed-off-by: Peng Huo <penghuo@gmail.com>

---------

Signed-off-by: Peng Huo <penghuo@gmail.com>
…ch-project#5643)

Add visitXyseries to PPLQueryDataAnonymizer so the xyseries stage
appears in the anonymized query logged for every PPL request, with
pivot literals and options masked. Previously the visitor fell through
to visitChildren and the entire xyseries clause was silently dropped
from log output.

Also flip the xyseries entry in docs/user/ppl/index.md from stable to
experimental (since 3.8), matching how other newly-introduced commands
are listed.

Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: opensearch-ci-bot <opensearch-infra@amazon.com>
…search-project#5631)

* Surface PIT-context exhaustion with an actionable error message

The Calcite engine opens a Point-In-Time (PIT) context to page over a query
it cannot push down to OpenSearch -- for example a stats/aggregation that
groups by a text field with no keyword sub-field, which forces a full doc
scan. A PIT allocates one reader context per shard, so a single such query
over a many-shard index can exhaust the node's search.max_open_pit_context
budget on its own; concurrent load makes it more likely.

Previously that failure surfaced to the user as the opaque internal message
"exception while executing query: Error occurred while creating PIT for
internal plugin operation" with no hint of the cause or the remedy.

Detect the PIT-context-limit rejection in the execute-time cause chain and
rethrow it as a PointInTimeLimitExceededException wrapped in an ErrorReport,
so the reason names the search.max_open_pit_context setting and the details
explain the two remedies (raise the setting, or optimize the query). Match on
the "too many Point In Time contexts" marker rather than the exception class,
since OpenSearchRejectedExecutionException is also raised for scroll and
thread-pool rejections. The scan walks the whole cause chain because the
rejection surfaces several layers deep (SQLException -> RuntimeException ->
ExecutionException -> per-shard rejection).

This targets the Calcite path only; the marker check guards against
self-referential cause loops.

Signed-off-by: Kai Huang <ahkcs@amazon.com>

* Address review: trim PIT remedy text, generalize resource-limit exception

Drop the "optimize the query" remedy from the PIT-context-limit details,
leaving the actionable "increase [search.max_open_pit_context]" instruction.

Replace the one-message PointInTimeLimitExceededException with a reusable
ResourceLimitExceededException in common/error, alongside ErrorReport and
ErrorCode.RESOURCE_LIMIT_EXCEEDED, so the type is not one-class-per-message
and is reachable across modules.

Signed-off-by: Kai Huang <ahkcs@amazon.com>

---------

Signed-off-by: Kai Huang <ahkcs@amazon.com>
…and profiling (opensearch-project#5568)

* Overriding profile endpoint with analyze endpoint with operator tree and profiling

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Updating AnalyzeResponse format

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Cleaner method to combine profile and analyze

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Adding tests and CI test fixes

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Ran ./gradlew spotlessApply for formatting

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Fixing narrowing type conversion

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Adding querySegments back to AnalyzeResponse body

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Adding docs, integ-test, handling when Calcite is disabled, 'fallback' on complex queries

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Updating integ-test correct version, previously tested recommendation, which is not a part of this PR

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Deleted FQN, updated/added testing

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Rebased and fixed type mismatch

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Updating integ-test to ignore recommendations (next PR)

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* Updating unit test to ignore \r\n and \n differences

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

* fixing spotless check

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>

---------

Signed-off-by: Krish Gandhi <kjg2352@gmail.com>
…ct#5654) (opensearch-project#5655)

The PPL rest command was added (opensearch-project#5599) and fully reverted (opensearch-project#5635) within
the same release cycle, so neither should appear in the notes.


(cherry picked from commit 7c24851)

Signed-off-by: Eric Wei <menwe@amazon.com>
Signed-off-by: opensearch-ci-bot <opensearch-infra@amazon.com>
Co-authored-by: Eric Wei <menwe@amazon.com>
* feat: slow query thread pool

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* handle slow query detection when optimization runs in execution step

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* fixes: assorted context propagation issues

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* fix remaining integ tests

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* add some more thread & security tests

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* code self-review, round 1

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* use 2x background threads to account for 2x pools

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* rename slow -> complex, add pool indication header

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* add a failure log for slow pool requests

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* fix profile, add thread pool as part of profile object

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* remove leftover build.gradle changes from another branch

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* add cancelation polling so ppl cancelation is faster to apply

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* Add thread pool profile details to doc

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* Move analyze call measurement

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* add complex pool IT

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* Move calcite context thread copies to dedicated method

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* add attach_pid to gitignore

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* register timeout handler to complex pool on these requests

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* fix units

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

* remove redundant optimize call from execution engine during execution

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>

---------

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
…earch-project#5646)

* Push down aggregation on text field without .keyword sub-field

For a text-typed group key or metric argument with no .keyword sub-field,
NamedFieldExpression.getReferenceForTermQuery() returned null and
CompositeValuesSourceBuilder/ValueCountAggregationBuilder rejected the null
field, so pushDownAggregate silently fell back to a full _source scan and
client-side aggregation.

Route those bare RexInputRefs through a Calcite script that reads the value
from _source, matching TermQuery/LikeQuery/RexStandardizer for filter and
script fields. Composite terms buckets and metric aggregations that accept
a script (notably count(FIELD)) now push down.

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

* Apply spotless formatting

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

* Update explain plan pins for text-field aggregation pushdown

Four CalciteExplainIT plans encoded the pre-fix behavior where dedup / chart /
timechart on a text field with no .keyword sub-field silently fell back to a
client-side aggregation over an unbounded scan. With the AggregateAnalyzer fix
those queries now push down as composite terms (script over _source), so the
pinned physical plans are stale.

- Rename testDedupTextTypeNotPushdown -> testDedupTextTypePushdown and update
  explain_dedup_text_type_push.yaml to the composite terms + top_hits DSL.
- Refresh chart_null_str.yaml (chart limit=10 ... over gender by age span=10)
  to the composite terms(script) + histogram plan.
- Refresh explain_timechart.yaml and explain_timechart_count.yaml (timechart
  span=1m ... by host) to the composite terms(script) + date_histogram plan.

Add DedupCommandIT.testDedupOnTextField to verify behavioral equivalence: the
result set for `source=bank | dedup email` matches the fixture's set of
distinct emails, running both under the V2 path (base class) and Calcite
pushdown path (CalciteDedupCommandIT).

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

* Verify dedup row values in testDedupOnTextField

Widen the assertion beyond the dedup key to also verify the associated
projected columns per row (firstname, balance), so the top_hits round-trip
in the pushed-down dedup DSL is checked end-to-end.

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

* Add timechart avg-by-text-host result IT

Assert row-level results for `source=events | timechart span=1m avg(cpu_usage)
by host` on the events fixture, where `host` is a text field with no .keyword
sub-field. Golden values were collected on upstream/main (unpushed) before
applying the fix, so the assertion pins behavioral equivalence between the
V2 client-side plan and the pushed composite terms(script)+date_histogram
plan.

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

* Trim comment on testTimechartAvgByTextHost

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>

---------

Signed-off-by: Peng Huo <penghuo@amazon.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
…rch-project#5664) (opensearch-project#5666)

* Add ci.opensearch.org/m2/ mirror for plugin resolution (sql)



* Address order issues



---------




(cherry picked from commit fe20ba6)

Signed-off-by: shreyah963 <shreyab963@gmail.com>
Signed-off-by: Peter Zhu <zhujiaxi@amazon.com>
Signed-off-by: opensearch-ci-bot <opensearch-infra@amazon.com>
Co-authored-by: Shreya Bhatta <shreyab963@gmail.com>
Co-authored-by: Peter Zhu <zhujiaxi@amazon.com>
…ect#5656)

* [Feature] Add PPL `rest` command (Calcite system row source)

Add a leading `rest <endpoint>` command that exposes a curated, read-only,
fixed-schema set of in-cluster management endpoints as a PPL table.

---------

Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
…s. (opensearch-project#5642)

* Clean docs
* Fix CalcitePPLRareTopNTest showperc tests
* Calculate percentages before filtering
* Added percentfield and changed decimal places from 2 to 6
* Update test errors, SUM to CHECKED_LONG_SUM
* Remove percent field check

Signed-off-by: Ajimelec Gonzalez <ajimelec@amazon.com>
…-project#5675)

PPLOperandTypes.SCALAR_TYPES declares DATE/TIME/TIMESTAMP/IP/BINARY
operands as UDTs, but typesMatch rejected a pair outright whenever only
one side extended AbstractExprRelDataType. The analytics engine builds
its row types from plain Calcite types (date -> TIMESTAMP(3), ip and
binary -> VARBINARY) plus markers deriving from Calcite's AbstractSqlType,
so every such operand failed the check.

The result was a self-contradictory error, because getAllowedSignatures
renders via the UDT tag while getActualSignature renders via
convertRelDataTypeToExprType -- both print TIMESTAMP:

  Aggregation function LIST expects field type
  {...|[DATE]|[TIME]|[TIMESTAMP]|[IP]|[BINARY]}, but got [TIMESTAMP]

Map the UDT tag to the SqlTypeNames a backend would emit for the same
logical type. Comparing backing types would not work, since the UDTs are
all VARCHAR-backed. The mapping is expressed over SqlTypeName rather than
by calling convertAnalyticsEngineRelDataTypeToExprType, because
analytics-api is a compileOnly dependency of core and loading those
marker classes throws NoClassDefFoundError wherever it is off the
runtime classpath.

Signed-off-by: Kai Huang <ahkcs@amazon.com>
@Hanyu-W Hanyu-W added the infrastructure Changes to infrastructure, testing, CI/CD, pipelines, etc. label Aug 5, 2026
Hanyu Wei added 8 commits August 4, 2026 21:24
Add a cross-repository CI check that keeps the OpenSearch-Dashboards PPL
lint rule 'unsupported-window-function-in-eventstats' and the SQL backend
in agreement.

Frontend half: a SQL-owned Node script loads the compiled OSD analyzer
from an OSD checkout and asserts the rule's diagnostic counts. Backend
half: a Gradle integration test sends the same queries to the live
/_plugins/_ppl endpoint of the SQL plugin built from the checkout. Both
halves consume one shared contract file.

- integ-test/.../ppl-lint/unsupported-window-function-in-eventstats.spec.json
- scripts/ppl-lint/run-frontend-contract.mjs
- integ-test/.../calcite/remote/PplLintRuleValidationIT.java
- .github/workflows/ppl-lint-rule-validation.yml
- scripts/ppl-lint-rule-validation.sh

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Addresses shellcheck SC2006/SC2046 on the chown/su lines so actionlint
runs clean. Behavior is unchanged.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
The OpenSearch CI container is Amazon Linux 2 (glibc 2.26), but OSD
requires Node 22 whose prebuilt binary needs glibc >= 2.27. Running the
Node frontend contract inside that container failed with 'GLIBC_2.27 not
found'.

Split into two required jobs: 'frontend' runs the OSD analyzer contract
on a bare ubuntu-latest runner (modern glibc, actions/setup-node works),
and 'backend' keeps the Gradle integration test in the CI container where
the OpenSearch test cluster needs it.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Generalize the single-rule PPL lint validation contract (eventstats PoC) into
a schema-v2 corpus that pins every reachable OSD analyzer rule to live
/_plugins/_ppl behavior. Both halves read the same reviewed contract files so
neither the analyzer diagnostic nor the engine behavior can drift without a
red build. Verified live end-to-end: frontend 13/13 against the OSD main
analyzer; backend 13/13 (pr) and 21/21 (nightly) against a live test cluster.

Contract schema v2 (integ-test/src/test/resources/ppl-lint/contracts/*.spec.json
+ manifest.json):
- backend.kind discriminator: rejection | result-shape | advisory (explain
  reserved for the nightly-only explain rule class once it lands on OSD main).
- per-contract backendFixture.clusterSettings so contracts that disagree on
  fallback/join settings each set what they need (eventstats needs
  calciteFallback=false; dedup-consecutive needs true) — validated in one run.
- per-case minVersionRequired/engineRequired so both halves skip identically.
- wiring block asserted deep-equal against the OSD catalog (drift tripwire).
- frontendContext.deriveFromMapping single-sources fields/typeMap for the
  field-validation existence pass.
- error.reason values snapshotted from the observed engine response, not
  hand-typed (join/multisearch AST-build-time throws yield generic
  "Invalid Query"; union/replace carry the specific message).

Frontend adapter (run-frontend-contract.mjs): contract discovery via manifest,
schedule + version/engine gating, catalog wiring assertion, compiled-simplified
and runtime-bundle grammar surfaces (runtime-only rules whose parser rules are
absent on the checkout's grammar assert wiring then skip cleanly), collect-all
failures, and a frontend-report.json for disagreement diffing. Nightly adds a
coverage assertion that every enabled catalog rule has a contract.

Backend IT: parameterized over the contract corpus with per-kind verifiers
(verifyRejectedCase / verifyResultShape / verifyAdvisory200), per-contract
cluster-setting apply+reset, GET / cluster-version gating, and a
backend-report.json recording observed status/type/reason per rejection.
Sends queries with a JSON-escaped body so contract queries containing quotes
(grok field=body "...") reach the engine faithfully instead of tripping a core
request-payload parse error. Honors -Dppl.lint.schedule=pr|nightly, forwarded
to the forked test JVM via integ-test/build.gradle.

Workflow + repro script: derive schedule (PR -> pr, cron -> nightly), read the
contract dir, upload frontend/backend reports + corpus artifacts.

Rules covered: eventstats window fn, division-by-zero, head-without-sort,
disabled-join-type, field-validation (shape + existence) on PR; plus
dedup-consecutive and the runtime-only union/multisearch/replace on nightly.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Take the cross-repository PPL lint check from the PoC to the design in
ppl-lint-ci-validation-design.md. The detector half now lints against the
*candidate* runtime grammar bundle the SQL PR builds — through OSD's production
headless lint API — instead of the compiled analyzer or a hand-rolled reparse
of OSD main's checked-in grammar. Both halves validate the same grammar, so a
parser/semantic change that invalidates a lint rule reds the build.

SQL-side changes (the OSD headless API ships separately):

- PplLintRuleValidationIT: export the candidate grammar bundle
  (GET /_plugins/_ppl/_grammar) + a target manifest {engineVersion, grammarHash,
  grammarBundle} while the cluster is alive; read schema-v3 specs; select the one
  expectations[] entry matching the backend version (zero/>1 fails); record the
  observed backend behavior per query for the differential.
- integ-test/build.gradle: forward -Dppl.lint.grammar.bundle / -Dppl.lint.target
  to the test JVM alongside the existing ppl.lint.* knobs.
- run-frontend-contract.mjs: deserialize the candidate bundle via the OSD
  headless API and lint each query with lintQueryWithBundle (runtime-bundle
  surface, so the runtime-only arity rules fire); pin dataSourceVersion +
  knownVersion to the candidate version; assert the detector-vs-backend
  differential from the backend report; fail loud on a missing bundle.
- workflow: linear backend-validation -> detector-validation -> validation-result
  pipeline; artifacts are the only bridge between jobs. validation-result is the
  single always() required check (red unless both jobs succeed) and writes the
  per-rule PR summary; assemble-run-manifest.mjs emits run-manifest.json with the
  immutable SQL + OSD SHAs, mode, backend version, grammar hash, and enforced set.
  A workflow_dispatch osd_ref run is pre-merge evidence, not a protection result.
- contracts: migrate all 9 specs to schema v3 (named queries{role,query} +
  version-scoped expectations[]); partition manifest.json into enforced
  (eventstats, multisearch, union, replace), pendingReview (field-validation),
  and nonEnforcing. Union/multisearch triggers are query-initial, not pipe-first:
  OSD prepends a synthetic source prefix to pipe-first queries, which would
  desync the two halves.
- Add scripts/ppl-lint/README.md documenting inputs, local reproduction, the
  contract format, and the failure table.

Verified end to end against a live cluster: the backend IT exports a real
candidate bundle and the detector runner agrees on all four enforced rules
(triggers rejected + 1 diagnostic, controls accepted + 0); an intentional
expectation mismatch reds the runner.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
The detector-validation step piped the runner through `tee`, so the step took
tee's (success) exit status and a real runner failure — e.g. the OSD headless
module being absent on OSD main — went green as a vacuous pass. The first live
PR run hit exactly this: the runner exited 2 with "Expected OSD module not
found ... headless_ppl_lint", yet detector-validation and validation-result
both reported success. Add `set -o pipefail` so node's non-zero exit propagates
and the required check correctly reds until the OSD headless API merges.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
detector-validation hardcoded repository: opensearch-project/OpenSearch-Dashboards,
so osd_ref could only resolve commits/branches that exist upstream. An unmerged
OSD change on a fork (e.g. the headless lint API before it lands on OSD main)
could not be validated end to end.

Add an osd_repo workflow_dispatch input (default
opensearch-project/OpenSearch-Dashboards) that the OSD checkout honors, thread
osd_repo through the detector job output into the run manifest + PR summary, and
treat any non-upstream-main target as osd-branch-evidence (never a required
check). The required pull_request run is unchanged: it still checks out upstream
OSD main.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Hanyu Wei added 27 commits August 4, 2026 21:25
…mpiled one

The discovery job hardcoded `PPL_LINT_SURFACE=compiled-simplified`. That was an
unnecessary restriction: `lint_runner` SKIPS the four `runtimeOnly` rules on the
compiled grammar because the productions they walk do not exist there, so a
compiled-only run cannot observe them at all — and three of the four ship at error
severity, where a false positive is most expensive.

The job now exports the engine's grammar via GET /_plugins/_ppl/_grammar and lints
on the runtime surface, falling back to the compiled surface with a warning if the
export fails. Best-effort rather than fatal: a lead-generator that produces nothing
because one endpoint was unavailable is worse than one with narrower coverage, and
the surface is recorded in the report so a reader can tell which ran.

Also fixes an unbound-variable crash in that step. Expanding an empty array as
"${extra[@]}" under `set -u` is an error in bash before 4.4, so the compiled-surface
fallback would have died — the one path that only runs when something else already
went wrong. Verified both branches.

Worth recording since it bounds what harvesting can achieve: the four runtimeOnly
rules are at zero harvested queries and no surface changes that. OSD's lint tests
contain no trigger for union-min-datasets, multisearch-min-subsearch or
replace-wildcard-asymmetry; the only place they appear is a negative assertion that
they no-op on the compiled surface. Harvesting cannot invent what was never written.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
…had one

The partial-vs-full relaxation verdict reads the ENFORCED corpus, and 8 of its 11
contracts pinned exactly one trigger. With one trigger, "every trigger relaxed" is a
single observation, so the verdict cannot distinguish a full engine fix (version-scope
the rule away) from a partial one (narrow the detector) — and those need opposite
actions. The classifier warns about it, but the fix is more triggers.

Each new trigger exercises a DIFFERENT shape of the same condition, so a partial
engine fix is visible as a disagreement between them rather than as a uniform flip:

  union-min-datasets           single dataset that carries a pipeline (| fields)
  multisearch-min-subsearch    single subsearch that carries a pipeline (| where)
  replace-wildcard-asymmetry   reversed asymmetry (2 wildcards -> 1, not 1 -> 2)
  invalid-capture-group-name   hyphen, not just underscore
  unsupported-window-function  dense_rank, not just rank
  dedup-consecutive-unsupported  multi-field dedup with consecutive=true
  division-by-zero             decimal 0.0, not just integer 0
  head-without-sort            head after a where stage, not a bare source

Every expectation was verified on a live 3.8 engine rather than inferred, including
the exact error type and reason string:

  union/fields        400 IllegalArgumentException  Union command requires ... Provided: 1
  multisearch/where   400 SyntaxCheckException      Invalid Query
  replace 2->1        400 IllegalArgumentException  pattern has 2 wildcard(s), replacement has 1
  rex hyphen          400 IllegalArgumentException  Invalid capture group name 'user-name'.
  eventstats dense    400 CalciteUnsupportedException  Unexpected window function: dense_rank
  dedup multi-field   200 (advisory; succeeds via the Calcite-to-v2 fallback)
  head after where    200 (advisory)
  balance / 0.0       200 with the ratio column all-null

Detector counts and severities were confirmed by running the real detector runner
over the corpus: all four compiled-surface triggers score 1 at the contracted
severity, and eventstats-dense-rank scores 1/error once a version is supplied. The
four runtime-bundle-only contracts are reported not-applicable on the compiled
surface, as before. Failure count is unchanged from baseline (8, all pre-existing
"enabled catalog rule has no contract file" coverage warnings).

Worth noting for review: the pre-3.8 expectations for eventstats-dense-rank reuse the
existing rank() pins (500 / UnsupportedOperationException) by analogy — both are
CalciteUnsupportedException on 3.8, and only 3.8 was available to verify. The 3.6 and
3.7 legs will confirm or correct them.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Seven hypotheses for the 2.19 observation-leg timeout have each been refuted by
observation: the index-wipe race, a Gradle cluster fallback, HTTP/2 negotiation,
FIPS, the bundled plugin set, a port collision, and address family. What is
established is narrow and contradictory: the engine is alive and logging
throughout, its publish address and network topology are identical to the passing
3.5.0 leg, the Gradle args and task graphs are byte-identical, curl reaches every
endpoint the framework calls in 0s from the same runner -- and GET _nodes/plugins
from the test JVM never returns.

curl has said everything it can. This probe asks the JVM instead, one layer at a
time against the same address: raw TCP connect, then HttpURLConnection, then the
real OpenSearch RestClient on each endpoint the framework itself calls, each
timed and bounded at 15s. Whichever layer stops working localizes the fault --
network, JDK HTTP stack, async client, or a specific response.

It deliberately does not extend the framework's base class, since that base class
is what hangs; inheriting it would reproduce the symptom instead of isolating it.
Reports rather than asserts (the leg is already failing and the evidence is the
point), except that a failed TCP connect is fatal because nothing below it would
mean anything. Wired into the compiled leg with continue-on-error so a diagnostic
can never be what decides the leg.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
…he PR subset

Five contracts declared `schedule: "nightly"`, so a pull_request run skipped them:
dedup-consecutive-unsupported, disabled-join-type, division-by-zero, field-validation
and head-without-sort. That left the required check scoring 3 of 19 rows on a PR, and
it hid the triggers the multi-version relaxation rollup depends on — a rule with no
scored case contributes no trigger census, so full-vs-partial cannot be judged for it
on a PR at all.

All 11 contracts now declare `schedule: "pr"`. A PR run reaches the whole corpus:
35 queries, 19 scored on the compiled surface (the other 16 are the runtime-bundle-only
contracts, not-applicable there as before), up from 3 of 19.

This DOES make the four advisory rules blocking, and that is a deliberate accepted
trade rather than an oversight. Neither PplLintRuleValidationIT nor
run-frontend-contract.mjs consults the manifest's `enforced` list — a contract that
runs is a hard assertion — so `schedule` was the only thing keeping them
non-blocking. Their oracles are genuinely weaker than the error rules': an advisory
rule's query SUCCEEDS, so the contract can only assert a result shape or plain
acceptance, and dedup-consecutive in particular depends on the Calcite-to-v2 fallback
staying enabled. If one of them goes red, check the oracle before editing a rule.

The manifest description, the IT javadoc and the README all claimed the split
controlled blocking. Corrected: `enforced` / `nonEnforcing` record oracle quality and
review status — how much to trust a red result — not whether one can occur. The
schedule filter itself is kept, since it remains the only way to hold a new contract
back from PR runs while its oracle settles.

Verified: the full corpus exits 0 on the PR schedule with zero skips, against a live
3.8 engine and the real ACCOUNT fixture.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
The probe reported nothing: its step logged BUILD SUCCESSFUL in 2m45s with no
probe output at all. It used JUnit 4's org.junit.Test while this module runs
useJUnitPlatform(), so the class was collected as zero tests and the task
succeeded vacuously -- the same shape of failure the PPL lint contract itself
guards against, in the diagnostic meant to explain it.

Switch to org.junit.jupiter.api.Test, matching PplLintRuleValidationIT. Also stop
the step's grep from hiding evidence: add --info and match 'tests completed' and
'No tests found' so a zero-test run is visible next time instead of reading as a
pass.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
…ually rejects

Moving every contract onto the PR schedule turned the required check red with 8
failures, 7 of them this: the trigger cross-check paired the detector against
`be.rejected`, which is only meaningful for a `rejection`-kind rule.

An advisory rule flags a query the engine runs happily — head-without-sort marks
non-determinism, division-by-zero marks a silent null, dedup-consecutive succeeds via
the Calcite-to-v2 fallback. For those, "detector flagged, backend accepted" is the
rule working as designed, so the check failed every advisory trigger
unconditionally, including ones that predate this branch. That, not runtime cost, is
the structural reason those contracts could only ever run nightly; I had attributed
it to cost and weaker oracles, which was wrong.

The check now runs only when the contract declares `backend.kind: "rejection"`. The
contracts already carry that distinction, so this reads data that exists rather than
adding a flag, and rejection rules are completely unaffected.

Advisory triggers keep full coverage from the two other assertions, which is why
relaxing the pairing is safe rather than merely convenient:
  - the backend-kind check still fires if the engine starts REJECTING a query the
    contract pinned as accepted;
  - the `detectorCount` assertion still fires if the detector stops flagging it.
Every trigger in the corpus pins detectorCount: 1 regardless of kind, so a silent
advisory detector is still caught.

Verified by replaying the failed CI run's own artifacts (backend-report.json,
target.json, ppl-grammar-bundle.json downloaded from run 30289514275): same inputs go
from 6 failures to 0 on the compiled surface. Confirmed still selective by tampering
the backend report to make `disabled-join-type/right-join-disabled` — a rejection rule
— look accepted: that fails with both the backend-kind and the trigger assertion.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Second vacuous pass from the same probe, different cause. The JUnit 5 switch was
wrong: integTestRemote runs the default JUnit 4 runner -- only integJdbcTest calls
useJUnitPlatform() -- so the jupiter @test made the class undiscoverable and
Gradle reported 'No tests found for given includes: [**/*IT.class]' while the step
still looked fine.

But the JUnit 4 annotation alone was not enough either. Gradle only discovers an
IT that inherits a runner from a framework base class; a standalone class has none
and is collected as zero tests. That is why the FIRST version reported nothing
despite compiling, matching the include pattern, and having its .class file in
place.

Extend OpenSearchTestCase: it supplies the randomized-testing runner but builds no
REST client, so the probe is discovered without inheriting the client setup that
hangs -- which was the whole reason for not extending the REST base class.

Verified locally against a live cluster, all four layers reporting:
  tcp connect OK in 2ms
  HttpURLConnection _nodes/plugins OK in 8ms: HTTP 200, 9456 bytes
  RestClient _nodes/plugins OK in 54ms: HTTP 200, 9456 bytes
  RestClient _plugins/_ppl/_grammar OK in 20ms: HTTP 200, 248625 bytes

Note for anyone running it by hand: the opensearch.rest-test plugin requires
tests.rest.cluster, tests.cluster and tests.clustername to be all-null or
all-non-null, or the project fails to configure before any test runs.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
…riants

Two fixes.

1. Test the LATEST PATCH, not <line>.0. The compiled matrix pinned 2.19.0 while
   the published line is at 2.19.6 -- six patches stale, so it validated an engine
   no user runs and would have attributed any bug fixed in between to the whole
   2.19 line. Checked every line on Docker Hub: 2.19.6 is the only stale pin;
   3.0.0 / 3.5.0 / 3.6.0 / 3.7.0 already are their lines' latest patch. Comments
   now say to keep them latest rather than .0.

2. Probe client variants. The previous probe localized the 2.19 timeout precisely:
     tcp connect                        OK      4ms
     HttpURLConnection _nodes/plugins    OK     27ms  HTTP 200, 15844 bytes
     RestClient _nodes/plugins           FAILED 15396ms  SocketTimeoutException
     RestClient _cluster/health          FAILED 15025ms  SocketTimeoutException
   Every endpoint fails, including a 459-byte health response, while the JDK's own
   HTTP stack succeeds against the same URL. So the fault is in how the async
   client speaks to this engine -- not the network, engine, response size, or any
   one endpoint, which is why seven log-derived theories all missed it.

   Rather than guess again, run candidate configurations side by side against the
   same endpoint: FORCE_HTTP_1, NEGOTIATE, FORCE_HTTP_2, and a fresh single-
   connection manager. Whichever succeeds names the fix; if none do, the client
   cannot be configured around it and the answer is a client/engine version
   constraint instead.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
With the version pin corrected to 2.19.6, the leg gets past the client entirely
and fails on the real incompatibility instead:

  PUT /_cluster/settings -> 400
  persistent setting [plugins.calcite.enabled], not recognized

Calcite is a 3.x feature and SQLIntegTestCase.init() sets that setting
unconditionally, so every 2.x leg aborts before seeding a fixture or running a
query.

In observe-only mode, catch exactly that failure and continue without the
setting: a pre-Calcite engine is a legitimate thing to observe, and each
contract's own frontendContext.isCalcite already states what the linter should
assume there. super.init() aborts partway when it throws, so redo the
version-independent half (increaseMaxCompilationsRate).

Matched narrowly -- on the setting name plus "not recognized", not on any 400 --
so a genuinely broken settings call on a Calcite-capable engine still fails
rather than being waved through as "old engine". Asserting mode is unchanged:
the required check runs against the PR's own build, where a missing Calcite
setting is a real problem.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
…gine

The init() tolerance worked -- the log shows 'observing without it' -- but the leg
still failed, because applyClusterSettings re-applies the same setting per
contract from backendFixture.clusterSettings, undoing it once per contract.

Every setting in that block is Calcite-family (calcite, calciteFallback,
allJoinTypesAllowed), and a pre-Calcite engine rejects all of them identically. So
record support once in init() and skip the block, rather than wrapping each call
in the same catch.

Verified in the same run that the earlier fix landed: 2.19.6's REST client is
healthy (_nodes/plugins 312ms, _cluster/health 6ms, FORCE_HTTP_1 / NEGOTIATE /
fresh-conn-manager all OK; only FORCE_HTTP_2 fails, correctly, since 2.19 has no
h2). There was never a client bug -- 2.19.0 was six patches stale.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Reverts 4c80f7b. That commit claimed HTTP/2 negotiation as the cause of the
2.19 observation-leg timeout. It was not, and the pin fixed nothing.

The real cause was the version pin: the leg tested opensearchproject/opensearch:
2.19.0, six patches behind the 2.19.6 the line actually ships. On 2.19.6 the
unmodified client is healthy -- _nodes/plugins in 312ms, _cluster/health in 6ms --
and the variant probe confirms it is not protocol-related at all:

  FORCE_HTTP_1        OK  18ms
  NEGOTIATE           OK  21ms
  fresh-conn-manager  OK  12ms
  FORCE_HTTP_2        FAILED (correctly: 2.19 has no h2)

Since NEGOTIATE -- the default -- works, forcing 1.1 was a no-op dressed as a fix,
and leaving it in would have suggested a protocol constraint that does not exist.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
The multi-version check flagged engine-message-changed on 3.7.0:

  error.reason "There was internal problem at backend"
              -> "Unexpected window function: dense_rank"

The >=3.7.0 <3.8.0 expectation for eventstats-dense-rank was copied from the
pre-3.7 epoch and never updated, while its sibling eventstats-rank in the SAME
epoch already pins the function-naming wording. 3.7 names the offending function;
only the pre-3.7 engines emit the generic message.

Detector-side verdict is unaffected, so this is the update-contract remediation
the classifier recommended -- not a rule change.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Add channel-aware schema-v4 contracts for the approved twelve lint detectors and command-suggestion syntax feature. Keep default-off detector contracts dormant, enforce strict catalog wiring, and expand multi-version/discovery coverage across the active shipping corpus.

Add the disabled-object backend fixture, normalized frontend and backend oracles, required-lane annotations, and syntax-aware aggregation. The exact OSD shipping census remains report-only until the paired OSD default-alignment change lands.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Pin exact diagnostic, deterministic-fix, and AI-action behavior for all active schema-v4 contracts, and promote the complete 13-contract corpus to required PR validation.

Enforce the shipping census while retaining dormant detector contracts as report-only observations. Propagate frontend assertion failures through required and multi-version reports with inline annotations.

Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
Signed-off-by: Hanyu Wei <weihanyu@amazon.com>
@Hanyu-W
Hanyu-W force-pushed the codex/ppl-linter-multi-surface-restored branch from 4858b84 to 4381c28 Compare August 5, 2026 04:28
@Hanyu-W

Hanyu-W commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Superseded by upstream draft opensearch-project#5678. The branch was rebased onto current upstream main, made DCO-clean, and its compatibility design was aligned with the implemented three-configuration workflow.

@Hanyu-W Hanyu-W closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

infrastructure Changes to infrastructure, testing, CI/CD, pipelines, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.