docs: add an agent skill for auditing datafusion-spark expressions#23893
docs: add an agent skill for auditing datafusion-spark expressions#23893andygrove wants to merge 2 commits into
Conversation
Adds .ai/skills/audit-datafusion-spark-expression, which audits a single datafusion-spark function against Apache Spark 4.2.0. The skill reads the Spark source across 3.5.8, 4.0.4, 4.1.3, and 4.2.0 to detect cross-version divergence, reviews the Rust implementation's signature, coercion, nullability, and ANSI handling, and establishes expected values by running each candidate query through a local pyspark 4.2.0 rather than deriving them by reading Spark's source. Because .slt expectations are hard coded, a derived value that is wrong becomes a golden value that is wrong, so the skill also forbids --complete on files under test_files/spark/. Verified by running it against next_day, which surfaced the divergences fixed in a companion change.
| `CollationSupport.X.exec(..., collationId)` or declares | ||
| `StringTypeWithCollation` inputs | ||
|
|
||
| Produce a diff summary across 3.5.8 → 4.0.4 → 4.1.3 → 4.2.0. Note new or |
There was a problem hiding this comment.
do we need spark3, the agent should match against latest Spark release?
There was a problem hiding this comment.
If we want to re-use datafusion-spark expressions in Comet, we'll need them to be compatible across multiple Spark versions
|
|
||
| ## Step 4: Cross-Check Comet and Sail | ||
|
|
||
| The `datafusion-spark` README recommends checking both projects for existing |
There was a problem hiding this comment.
would this require having local clones for Comet/Sail?
There was a problem hiding this comment.
The skill would automatically clone the repos
There was a problem hiding this comment.
Or maybe it would just use http and look at the repo that way
|
|
||
| ## Tone and Style | ||
|
|
||
| - Write in clear, concise prose. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #23893 +/- ##
==========================================
- Coverage 80.65% 80.65% -0.01%
==========================================
Files 1091 1092 +1
Lines 371031 371106 +75
Branches 371031 371106 +75
==========================================
+ Hits 299256 299312 +56
- Misses 53935 53943 +8
- Partials 17840 17851 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Running the skill against pmod surfaced four gaps: - No warning that Arrow kernel semantics differ from Java's. Total-ordered float comparison hiding -0.0, checked rather than wrapping arithmetic, and Java's byte/short promotion to int each caused a real bug in that audit. - Guidance to match Spark's error message text conflicted with the abs.rs precedent it cited. Split by who constructs the error: match it when the function builds the message, assert only that an error is raised when an Arrow kernel does. - The stash-verify recipe cannot produce fail-before evidence for Rust unit tests, because they live in the file being stashed. - The ground-truth harness could be silently reused from a previous audit's scratchpad copy.
Which issue does this PR close?
No issue tracks this. It is tooling, proposed for discussion. Related:
#15914 (the
datafusion-sparkepic) and #23887 (version-specific
SLT expectations, which this skill's cross-version findings link to).
Rationale for this change
The
datafusion-sparkcrate reimplements Spark's built-in expressions.Correctness against Spark is the entire point of the crate, but there is no
routine process for checking an implementation against the Spark source, and no
record of which functions have been checked.
The testing situation makes this harder than it looks. Comet, the sibling
project, tests by differential execution: it runs each query in both engines
and compares, so expected values never need to be written down. DataFusion's
.sltfiles hard-code every expected result, with no Spark in the loop. Twoconsequences follow:
expectation. Reading
nullSafeEvaland predicting the output is how wronggolden values get written, and once written they look like verified
behavior.
has to be written with one version in mind.
Many files under
test_files/spark/were machine-generated from Sail's golddata and their values were never checked against a running Spark. An existing
passing expectation is a claim, not evidence.
What changes are included in this PR?
One file:
.ai/skills/audit-datafusion-spark-expression/SKILL.md, followingthe
.ai/skills/convention already described in the repositoryCLAUDE.md.No production code changes.
The skill audits one function per invocation, in eight steps: read the Spark
source across 3.5.8, 4.0.4, 4.1.3 and 4.2.0 to detect cross-version divergence;
harvest Spark's own unit tests and
sql-tests/results/*.sql.outgolden files;review the Rust implementation's signature and coercion, return nullability,
null propagation, overflow, type dispatch and ANSI handling; cross-check Comet
and Sail; review existing SLT coverage; build a gap matrix; establish ground
truth; then apply findings.
Three decisions are worth calling out for review:
Spark 4.2.0 is the declared baseline. Older versions are read only to
detect that behavior changed, never to set an expectation.
Expected values come from a real Spark. The skill sets up a
pyspark==4.2.0venv and runs every candidate query through it under both ANSI modes, including
expectations that already exist in the file. If PySpark cannot be installed the
audit continues from source-derived values, but every such expectation is
marked unverified rather than presented as observed.
--completeis forbidden for files undertest_files/spark/. That modefills expectations with whatever DataFusion currently returns, which on an
audit would silently cement the bug being audited as the golden answer.
The skill also establishes a convention for divergences it cannot fix: a
commented-out SLT query carrying the correct Spark result and a link to the
tracking issue, where uncommenting it is the contract for verifying a fix.
That convention is new to this tree and is the piece most worth arguing about.
Are these changes tested?
The skill was run end to end against
next_day, which is how it was debugged.That run produced #23892 and issues
#23889, #23890 and #23891.
Running it surfaced eleven defects in the skill itself, which are fixed here.
Two are worth citing as evidence that the exercise was not circular:
unset SPARK_HOMEsat in a different code block from thecommand that needed it, so it never took effect and the run reproduced the
exact
TypeError: 'JavaPackage' object is not callablefailure the text waswritten to prevent.
ValueErroras though it were aSpark error, which would have written a fabricated divergence into the
.sltas observed fact.
Review of the resulting
next_daywork then found a genuine SQL-reachablepanic in the audited function that the audit had missed, and the gap that hid
it, which is now a rule in the skill: cross the argument shapes with the NULL
cases and with both ANSI modes rather than checking each axis alone.
The skill carries no automated test. Its verification is that following it
produces a correct audit, which is what #23892 demonstrates.
It has since been run a second time, against
pmod, which found three realbugs now fixed in #23898 and four more
divergences filed as #23894, #23895, #23896 and #23897. That run surfaced four
further skill gaps, folded in here. The most valuable is a section on Arrow
kernel semantics not matching Java's: total-ordered float comparison hiding
-0.0, checked rather than wrapping arithmetic, and Java'sbyte/shortpromotion to
inteach caused one of thepmodbugs, and the skill had saidnothing about any of them.
The
datafusion-sparkREADME already points contributors to Comet and Sailwhen implementing a function. This is the same idea applied to checking one.