Skip to content

fix(generate): fail loudly on a headerless .wgt.csv source, don't misparse it#194

Merged
ake2l merged 1 commit into
developmentfrom
feat/wgt-entity-generate
Jul 6, 2026
Merged

fix(generate): fail loudly on a headerless .wgt.csv source, don't misparse it#194
ake2l merged 1 commit into
developmentfrom
feat/wgt-entity-generate

Conversation

@ake2l

@ake2l ake2l commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Reported ask was ".wgt.csv weighted source at the attribute level, not just whole-entity" - turned out inverted: <key source="x.wgt.csv"> (attribute level) already applies weights correctly (verified statistically: 79% vs 80% expected on a 3000-row sample). The real, confirmed bug is the opposite end: <generate source="x.wgt.csv"> (entity level) silently misparses the headerless value|weight format as a headered CSV, producing nonsense column names and fewer rows than the file has.
  • Full fix (wiring WeightedEntityDataSource into <generate>'s chunked pagination) is bigger and riskier than it first looks: ChunkSourceReader's loads_all path (what a weighted source would need) re-permutes its pool via get_distributed_data/get_unique_data - reusing it would double-apply selection on top of already-weighted draws, and a naive per-page rebuild risks silently duplicating rows across multiprocess workers. That's real surgery on shared pagination code, scoped as follow-up work, not this PR.
  • This PR only fails loudly instead of silently producing garbage for .wgt.csv at <generate>-level.
  • .wgt.ent.csv (a normal headered CSV with an extra "weight" column) is explicitly NOT touched - reading it plainly (no resampling, weight column as literal data) is coherent and an existing test (test_source_script/test_iterate_source_scripted.xml) already relies on exactly that for an unrelated feature. Confirmed it still passes.

Test plan

  • TDD: failing tests for both .wgt.csv-must-raise and .wgt.ent.csv-must-still-read-plainly, confirmed RED/GREEN.
  • Existing test_iterate_source_script (which reads a .wgt.ent.csv-named file for an unrelated reason) still passes.
  • Full suite: 1428 passed, 15 skipped.
  • mypy/ruff clean.

…parse it

<generate source="x.wgt.csv"> (the headerless value|weight format used by <key
source=...>) fell through to the plain CSV reader, which has no coherent way
to read it: it treats the first data row as a header, producing nonsense
column names and one fewer row than the file actually has. Confirmed
empirically - not a hypothetical.

Scoped down from full <generate>-level weighted-entity support: wiring
WeightedEntityDataSource into the chunked pagination path (ChunkSourceReader)
properly is real surgery on shared, multiprocess-sensitive code - the
"loads_all" branch it would need to reuse re-permutes its pool via
get_distributed_data/get_unique_data, which would double-apply selection on
top of already-weighted draws, and a naive per-page rebuild risks silently
duplicating rows across workers. That's follow-up work, not this fix.

".wgt.ent.csv" is a normal headered CSV that happens to carry an extra
"weight" column - reading it plainly (no resampling, weight column included
as literal data) is coherent, so it's explicitly NOT touched by this guard.
An existing test (test_source_script/test_iterate_source_scripted.xml)
already relies on exactly that read for an unrelated feature (source-script
CSV cell evaluation) and must keep working - verified it still passes.

TDD: wrote failing tests for both the .wgt.csv-must-raise case and the
.wgt.ent.csv-must-still-read-plainly case, confirmed RED/GREEN against each.
Full suite 1428 passed/15 skipped, mypy/ruff clean.
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@ake2l ake2l self-assigned this Jul 6, 2026
@ake2l
ake2l merged commit ae18644 into development Jul 6, 2026
16 checks passed
ake2l added a commit that referenced this pull request Jul 6, 2026
…evel (#197)

## Summary
- #194 blocked ALL \`.wgt.csv\` at \`<generate>\`-level, merged before
#196 (\`.wgt.csv\` accepts an optional header) landed. That makes it too
strict now: a HEADERED \`.wgt.csv\` reads coherently through the plain
CSV path (proper column names, just unweighted) - the same tolerance
\`.wgt.ent.csv\` already gets. Only a genuinely headerless \`.wgt.csv\`
has no coherent plain-CSV reading at all (first data row misread as a
header, producing nonsense column names).
- Narrowed the guard to sniff the file's own first row (same check
\`FileUtil.read_weight_csv\` itself uses to auto-detect a header) and
only raise when there is no header to fall back on.

## Test plan
- [x] Headerless \`.wgt.csv\` at \`<generate>\`-level still raises.
- [x] New: headered \`.wgt.csv\` at \`<generate>\`-level now reads
through (unweighted, coherent columns) instead of raising.
- [x] \`.wgt.ent.csv\` regression test still passes.
- [x] Full suite: 1442 passed, 15 skipped, mypy/ruff clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant