Add tools/synthdata: RePoE-driven synthetic userstore generation - #195
Conversation
Commit generators, not data: synthetic userstore databases are produced locally from RePoE (the same repoe-fork source the app fetches, plus mods.min.json for legal mod pools and tier ranges) and a statistics-only profile extracted from a real database (counts and histograms; no ids, names, items, or mod values). Scale and seed are parameters; output is deterministic per (seed, profile, RePoE version). tst_synthdata validates a generated database through the app's own UserStore/repo layer - every payload must parse into the typed poe:: structs (the ParseCachedItems path). It skips unless ACQ_SYNTH_DATA_DIR and ACQ_SYNTH_ACCOUNT are set, so the regular suite is unaffected. The committed fresh-account.json profile is extracted from the author's own small account. Suite: 36/36 passing; a generated 100k-item store parses fully (100,224 items, 4,085 tabs) in ~1s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMh7nZndnFDht8mCFV7EAV
|
You're welcome to address the issues below however you feel best--and that includes deciding that they should not be addressed, or addressed with doc-only changes. -- @gerwaric Thanks for putting this together. The generator/registry approach looks potentially very useful for redesign experiments, especially the decision to keep generated databases out of the repository and make scale configurable. I reviewed this as executable tooling and test infrastructure rather than as a documentation-only change: the PR adds roughly 2,000 lines across the Python generator/scanner, a C++ test, and test build configuration. I have several questions about the validation contract, long-term maintenance, and a few claims in the documentation. 1. What is intended to exercise the generator automatically?
As a result, regressions in areas such as these would not be caught by the checked-in suite:
Could we establish at least one repeatable validation path? Some possibilities:
I do not think the full 100k dataset needs to run in CI, but right now the main implementation and its test can drift independently without any normal test failure. 2. Could the scope of
|
Response to gerwaric's nine-point review of PR gerwaric#195: - ctest now exercises the generator on every run (gerwaric#1): a checked-in RePoE fixture subset (make_fixtures.py, tools/synthdata/fixtures/), generate.py --repoe-dir for offline loading, and a synthdata_generate CTest fixture running selftest.py, which generates twice from identical inputs and fails on any row-level difference (gerwaric#6). - tst_synthdata gains named per-probe assertions from a generation manifest that records each coverage axis and registry quirk as exact serialized bytes (testpushpleaseignore#2), and a schema-equivalence check against a store created by UserStore itself (gerwaric#5). The schema check runs before any UserStore migration can touch the generated file. - The manifest repro block content-hashes every input (profile, RePoE files, quirks registry) plus the effective timestamp (gerwaric#6); --items is documented as an approximate minimum and the CLI reports stash / character / probe populations separately (gerwaric#7). - quirks-registry provenance restructured (gerwaric#8): durable references only in source (the 2016 corpus is pinned to its exact archive.org snapshot URL), structured inspected records for non-linkable evidence, stress_case flags for unverified tolerance probes. - Docs (gerwaric#3, testpushpleaseignore#4, gerwaric#9): the GUI dogfooding session is recorded as one manual measured run with its exact scope; the privacy guarantee is narrowed to what is actually guaranteed plus an owner-consent policy for profiles; coverage/fidelity claims recalibrated to reviewed wording. - synthdata-freshness workflow: weekly version.txt-only check that warns (never fails), classifies drift by re-extraction, and keeps exactly one tracking issue while stale; issue-writing is isolated in a schedule-only job with the branch-executed job on a read-only token. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMh7nZndnFDht8mCFV7EAV
|
Thanks for the thorough review — this was exactly the level of scrutiny I hoped this would get. Everything below landed as a second commit so it maps cleanly onto your numbers. Point-by-point: 1. What exercises the generator automatically?
On snapshot staleness: 2. Scope of 3. GUI-testing statements 4. Privacy wording 5. Schema duplication 6. Determinism contract 7. 8. Registry provenance 9. Claim calibration Validation of the changes themselves: full |
Author Note: The goal was to create generated synthetic data using RePoE for the best chance to cover all current scenarios now and going forward. Added a quirks engine to manually inject known "inactive" items
What this is
Commit generators, not data. A Python generator under
tools/synthdata/that builds arbitrarily large, deterministic userstore databases from two account-free inputs:repoe_data.py) — samerepoe-fork.github.iosource the app fetches, plusmods.min.jsonfor legal mod pools, tier ranges, and display templates. Cached and keyed by upstreamversion.txt, so game updates invalidate naturally.profile.py) — tab-type mix, items-per-tab quantiles, league shares by rank, buyout density. Counts and histograms only, so a profile is committable where its source DB never is.fresh-account.json(committed) is a small new-player account shape.Scale is a parameter: 100k items ≈ 43s; ~2M extrapolates to ≈14 min — so the webview-scale spike's 1M target is one flag.
Legacy wire idiosyncrasies
Following up on the colour-string/legacy-items point:
--coverageemits three instruments —item.h(influences, corruption states, Standard-only survivals like crucible/scourge/logbooks, PoE2 parse fields);quirks-registry.json— 28 curated entries, each with provenance and an evidence status (verified-in-data / documented / reported / community). Sources include the real 4,658-item public-stash feed embedded in the 2016-10-15 web.archive.org snapshot of the developer docs (re-verified locally), the GGG changelog, and third-party parser workarounds (PoB, exile-diary, Procurement). Covers leading-zero-stripped tab colours ("ff", "bf00"),<<set:…>>prefixes, explicit-false booleans, pre-3.1 sockets without sColour, DV sockets,flags: [], string-tuple properties,stackSizeText, literal-null arrays, unmodeled keys (talismanTier), empty/homoglyph/Remove-only tab names, and more;quirks.py, a scanner that reports keys in any real userstore thatsrc/poe/types/doesn't declare — so an old account becomes a concrete catalog delta instead of a silent parse gap. (Your 43k-item DB scans fully modeled: 0 unmodeled keys.)One open question found along the way:
item.hmodelsprophecyTextas an array of strings, the developer docs say?string, and no corpus I could find contains a real prophecy item payload to settle it. The registry follows the app model and records the question.Validation
tests/tst_synthdata.cppopens a generated DB through the app's ownUserStore/repo layer and requires every stash and character payload to parse into the typedpoe::structs. Skips unlessACQ_SYNTH_DATA_DIR/ACQ_SYNTH_ACCOUNTare set, so the regular suite is unaffected.Notes
src/changes.docs/redesign/topics/synthetic-data.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01VMh7nZndnFDht8mCFV7EAV