Repository navigation
Conversation
An operator is configured by a form, and the form is generated from the descriptor's own annotations. The same annotations are enough to fill it without a human: an enum offers its constants, a column knob takes a column of the type its rule allows, a numeric knob its declared bounds, and a conditional field is filled only where the branch that shows it is taken. This is one file because it is one decision made repeatedly — what a knob may hold — and splitting it would put half of that decision on either side of a boundary. `@SampleColumn` names the column a knob means where a type cannot say it: the opening price among four numbers, the country code among several strings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A candlestick reads four prices and the day they belong to, which a type cannot tell apart; a choropleth reads a country code the same way, and a value to shade it by that a type rule does narrow. Both are what the annotation and the rule are for. These read operators that land before this change does, so they belong here rather than being added back afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The generator did two jobs in one file: derive a valid configuration for an operator from the annotations it already carries, and sweep that configuration into the variants a parity run needs. At two thousand lines it was the largest change in the export series and the only one that could not be read in one sitting. This change leaves the first job. The sweep follows as its own change on top of it, restoring the members verbatim. The two jobs share no member. The base pass reaches its values through buildObject, decide and valueFor; the sweep reaches its own through rowFills, columnFill and leafFill, and the four entry points that return variants are the only callers of either. Nothing was rewritten to make the seam: whole members moved out, and every line that stays is the line that was there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One configuration exercises one path through an operator. A parity run wants more than that: every value an enum offers, the optional knobs a default leaves empty, and a text knob carrying a value that breaks a Python literal if it was spliced rather than escaped. This adds the sweep on top of the generator that derives the configuration. Four entry points return variants: one for an operator configured from its annotations, and three for a hand-written fixture, which needs them more, since the configuration it pins is otherwise the only one its operator ever runs. The sweep moves one knob at a time rather than crossing them, so a failure names the knob that caused it. The exceptions are the two variants that move every knob of a kind at once, where bisecting by hand costs less than a run per field: all optional knobs filled together, and all free-text knobs carrying the hostile value together, numbered so no two knobs carry the same text. The four tests read the entry points rather than the members behind them: an enum offered once per value, a boolean flipped in a variant of its own, an optional knob the base leaves empty and this fills, and two free-text knobs that come out holding different text. Every member here was in the change before this one and is restored verbatim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Automated Reviewer SuggestionsBased on the
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8388 +/- ##
============================================
- Coverage 92.60% 92.55% -0.05%
+ Complexity 5043 5039 -4
============================================
Files 1252 1220 -32
Lines 53587 50774 -2813
Branches 6673 6185 -488
============================================
- Hits 49622 46995 -2627
+ Misses 2317 2212 -105
+ Partials 1648 1567 -81
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`variantsOf` swept an already-configured op for its enums alone. Every caller wants the optional and hostile-text variants with them, which is the entry point beside it, so this one was never called. Its scaladoc explained itself by contrast with that one, so the explanation moves there rather than going with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… above Splitting the sweep out of this file moved members by their declaration, which left the block of documentation above three of them behind. Each then sat over the member that followed, describing something else: a JSON pointer helper's text over the one that reads a field's key, a nested-row accessor's over the one that reads a field's declared example, and a free-scalar test's over the type-id registry. None of them is wrong about the member it was written for. They travel with it. The class's own header and the column-resolution comment lose the parts a reader gets from the code beside them: an assembly strategy the next twenty lines carry out, and a numbered list of the tiers an `orElse` chain already reads as. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The optional-scalar fill repeated, sentence for sentence, why the column fill beside it exists. It points there instead. The pointer helper's one-line description said what its one-line body says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
I checked the sweep part of this stack. It looks good.
A free-text knob already gets a hostile value. A column knob carries a name the generated code writes into a subscript, which is a separate splice site, so it gets a run of its own: each knob moves to a hostile-named column of the type it already reads, one per type per port so two knobs never collide. Matched on the type the knob HOLDS rather than the types its rule admits: a filter's value is parsed as the held column's type, and moving the knob to a string would fail on this generator rather than on any escaping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1ed4742 to
ed6b700
Compare
|
@carloea2 this moved after your approval, one commit: every column knob now points at a hostile-named column. Would you take another look when you have a moment? |
ConfigGenerator names @SampleColumn in five places, and the annotation itself was in the change that introduces the trait, where nothing reads it. It is test-only metadata: it names the column in the shared fixture that should fill an @AutofillAttributeName field, so a parity run reads a realistic value rather than whatever the first column happens to hold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The annotation is applied on operator fields, so it has to exist before the first change that annotates one, which is ten steps earlier than this. It goes there instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
The generated variants now cover hostile column names and the schema annotations. Looks good.
The column pickers read a field's `attributeTypeRules` and then, finding no column of an accepted type, took another one anyway. That writes a configuration nobody would: the operator states which types the field takes, and a run over a column outside them reports the operator as covered on a configuration it does not describe. Both pickers now say why instead, naming the types the field accepts and the columns the port actually holds. Neither is reached against today's fixtures, every type any rule names having a column in each of them, so the whole job of this is to speak up the day one of them narrows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The char delimiter constant lists the picker's presets under `examples`, and a field that offers more than one example is swept like an enum. Old CSV's default is already a comma, so its optionals variant read the field as unset and filled the canonical "1", which split the fixture's numbers apart. A value with no letter or digit is labelled by its code points, since the runners fold every other character of a label to `_` and the five delimiters shared one directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 621 | 0.379 | 15,804/21,997/21,997 us | 🔴 +10.1% / 🔴 +39.9% |
| 🔴 | bs=100 sw=10 sl=64 | 1,567 | 0.957 | 60,944/87,729/87,729 us | 🔴 +18.3% / 🟢 +53.0% |
| 🔴 | bs=1000 sw=10 sl=64 | 1,988 | 1.214 | 494,855/596,247/596,247 us | 🔴 +10.3% / 🟢 +87.8% |
Baseline details
Latest main 40b507d from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 621 tuples/sec | 668 tuples/sec | 790.31 tuples/sec | -7.0% | -21.4% |
| bs=10 sw=10 sl=64 | MB/s | 0.379 MB/s | 0.408 MB/s | 0.482 MB/s | -7.1% | -21.4% |
| bs=10 sw=10 sl=64 | p50 | 15,804 us | 14,786 us | 12,375 us | +6.9% | +27.7% |
| bs=10 sw=10 sl=64 | p95 | 21,997 us | 19,972 us | 15,719 us | +10.1% | +39.9% |
| bs=10 sw=10 sl=64 | p99 | 21,997 us | 19,972 us | 19,058 us | +10.1% | +15.4% |
| bs=100 sw=10 sl=64 | throughput | 1,567 tuples/sec | 1,677 tuples/sec | 1,025 tuples/sec | -6.6% | +52.9% |
| bs=100 sw=10 sl=64 | MB/s | 0.957 MB/s | 1.023 MB/s | 0.626 MB/s | -6.5% | +53.0% |
| bs=100 sw=10 sl=64 | p50 | 60,944 us | 57,963 us | 98,759 us | +5.1% | -38.3% |
| bs=100 sw=10 sl=64 | p95 | 87,729 us | 74,137 us | 105,327 us | +18.3% | -16.7% |
| bs=100 sw=10 sl=64 | p99 | 87,729 us | 74,137 us | 120,491 us | +18.3% | -27.2% |
| bs=1000 sw=10 sl=64 | throughput | 1,988 tuples/sec | 2,058 tuples/sec | 1,059 tuples/sec | -3.4% | +87.7% |
| bs=1000 sw=10 sl=64 | MB/s | 1.214 MB/s | 1.256 MB/s | 0.646 MB/s | -3.3% | +87.8% |
| bs=1000 sw=10 sl=64 | p50 | 494,855 us | 480,340 us | 965,886 us | +3.0% | -48.8% |
| bs=1000 sw=10 sl=64 | p95 | 596,247 us | 540,437 us | 1,007,533 us | +10.3% | -40.8% |
| bs=1000 sw=10 sl=64 | p99 | 596,247 us | 540,437 us | 1,031,622 us | +10.3% | -42.2% |
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,322.15,200,128000,621,0.379,15804.12,21997.23,21997.23
1,100,10,64,20,1275.98,2000,1280000,1567,0.957,60944.11,87729.41,87729.41
2,1000,10,64,20,10058.61,20000,12800000,1988,1.214,494854.82,596246.71,596246.71The scan sources take apache#8341's resolution: the Limit 0 sample now lives in the shared inferSampleSize, and the CSV delimiters use the picker constant. JSONL reads its sample through inferSampleSize too. CSVOld's optionals variant is red until the delimiter examples land in apache#8388. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
The delimiter sweep works: both CSV sources offer all five presets, and their directory labels stay distinct. All five sweep tests and scalafmtCheckAll pass on the unchanged branch.
Command: sbt -batch "WorkflowCompilingService/testOnly *ConfigGeneratorSpec -- -oDF -z generateVariants" "scalafmtCheckAll".
The full suite has ten passes and the two missing chart-metadata failures already described in #8361. This branch also carries an older copy of that base generator. Please update the stack after addressing the incompatible join-key selection reported there. No separate finding in the new sweep changes.
Hash Join ties its build key's type to the probe key's through
"const": {"$data": ...}, which the generator never read, so two ports
listing their columns in different orders joined an INTEGER to a
STRING, and two ports sharing no type still produced a configuration.
Read the link from both ends and intersect it with the enum rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…guration-variants Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merging the configuration branch in left listColumnFill's comment on generateVariants as well, so it appeared twice and generateVariants lost its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What changes were proposed in this PR?
One configuration exercises one path through an operator. A parity run wants
more than that: every value an enum offers, the optional knobs a default leaves
empty, and a text knob carrying a value that breaks a Python literal if it was
spliced rather than escaped.
This adds the sweep on top of the generator that derives the configuration. Four
entry points return variants: one for an operator configured from its
annotations, and three for a hand-written fixture, which needs them more, since
the configuration it pins is otherwise the only one its operator ever runs.
The sweep moves one knob at a time rather than crossing them, so a failure names
the knob that caused it. The exceptions are the two variants that move every
knob of a kind at once, where bisecting by hand costs less than a run per field:
all optional knobs filled together, and all free-text knobs carrying the hostile
value together, numbered so no two knobs carry the same text. Two knobs sharing
one value is not a smaller version of the same test. Where both name an output
column, it writes the same column twice and the run fails for a reason that has
nothing to do with escaping.
Any related issues, documentation, discussions?
Part of #8325, 22 of 27; that issue lists the set in order. This is the second
half of the configuration generator, and #8361 is the first. The two are one
Scala object, so this branch carries that one's commit as well: the sweep does
not compile without the members it defines. Until #8361 merges the file view
here shows both halves, and once it does this narrows to the sweep on its own.
The last commit is the sweep, and it is the one to read.
Closes #8424, the task this change is the whole of.
How was this PR tested?
Four tests read the entry points rather than the members behind them: an enum
offered once per value with each variant labelled by the knob it moved, a
boolean flipped in a variant of its own, an optional knob the base leaves empty
and this fills, and two free-text knobs that come out holding different text.
The sweep is exercised end to end once the last pieces of the set land, when
every operator runs through the engine and through its generated script and the
two answers are compared. These four cover it in the meantime, and they name the
knob rather than the operator when they fail.
Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)