Public-Dataset Integrated E2E Test Plan¶
This document defines integrated, public-dataset end-to-end scenarios for TabDat-Explore across Phases 1 through 9 and the current Phase 13 linear-econometrics surface. The scenarios are written for AI agents and keep success criteria machine-observable: exit codes, stdout substrings or regexes, stderr expectations, and file existence at fixed paths.
Coverage Matrix¶
| Scenario | Phase 1 | Phase 2 | Phase 3 | Phase 4 | Phase 5 | Phase 6 | Phase 7 | Phase 8 | Phase 9 | Phase 13 | Phase 24 P0 |
|---|---|---|---|---|---|---|---|---|---|---|---|
s1_titanic_batch_core |
yes | yes | yes | no | no | no | no | no | no | no | no |
s2_interactive_shell_contract |
yes | no | yes | yes | yes | yes | no | no | no | no | no |
s3_taxi_lazy_scale |
yes | no | yes | yes | no | yes | yes | no | yes | no | no |
s4_penguins_script_repro |
yes | yes | yes | yes | no | yes | no | yes | yes | no | no |
s5_titanic_phase13_dogfood |
yes | no | yes | no | no | no | no | no | no | yes | no |
s6_canonical_parquet_workflow |
yes | yes | yes | no | no | no | yes | yes | yes | no | yes |
Global Harness Rules¶
harness:
repo_cwd: /Users/saehwan/repos/tabdat-explore-dev
shell: zsh
install_command: uv sync
artifact_root: artifacts/e2e
stdout_capture: required
stderr_capture: required
exit_code_capture: required
file_existence_checks: required
file_content_checks:
- svg_files_must_start_with: "<svg"
- parquet_files_must_exist: true
forbidden_actions:
- do_not_modify_src
- do_not_modify_tests
- do_not_treat_terminal_color_as_pass_fail
Dataset Acquisition Manifest¶
datasets:
- dataset_id: titanic
source_kind: csv
source_url: https://raw.githubusercontent.com/mwaskom/seaborn-data/a29a0141d20e156043ec257a64c8de3b3a03fd6e/titanic.csv
source_license_note: seaborn sample dataset
sha256: 81787d320d7f7b03df935e91de8bd19e11d45c5bbcab86ef4d4a76dc91b7d4f2
local_csv_path: artifacts/e2e/data/titanic.csv
local_parquet_path: artifacts/e2e/data/titanic.parquet
fetch_command: >
curl -L https://raw.githubusercontent.com/mwaskom/seaborn-data/a29a0141d20e156043ec257a64c8de3b3a03fd6e/titanic.csv
-o artifacts/e2e/data/titanic.csv
convert_command: >
uv run python -c "from pathlib import Path; import duckdb, sys;
src=Path(sys.argv[1]); dst=Path(sys.argv[2]); dst.parent.mkdir(parents=True, exist_ok=True);
con=duckdb.connect();
con.execute('copy (select * from read_csv_auto(?)) to ? (format parquet)', [str(src), str(dst)]);
con.close()"
artifacts/e2e/data/titanic.csv artifacts/e2e/data/titanic.parquet
- dataset_id: penguins
source_kind: csv
source_url: https://raw.githubusercontent.com/mwaskom/seaborn-data/4e06bf0b8c4bf161ed04e9df59b77c35fd2ec44a/penguins.csv
source_license_note: seaborn sample dataset
sha256: e07636bd8af74260099ea2f8678e2eabbf35def579940cc76f67061ee16c06c1
local_csv_path: artifacts/e2e/data/penguins.csv
local_parquet_path: artifacts/e2e/data/penguins.parquet
fetch_command: >
curl -L https://raw.githubusercontent.com/mwaskom/seaborn-data/4e06bf0b8c4bf161ed04e9df59b77c35fd2ec44a/penguins.csv
-o artifacts/e2e/data/penguins.csv
convert_command: >
uv run python -c "from pathlib import Path; import duckdb, sys;
src=Path(sys.argv[1]); dst=Path(sys.argv[2]); dst.parent.mkdir(parents=True, exist_ok=True);
con=duckdb.connect();
con.execute('copy (select * from read_csv_auto(?)) to ? (format parquet)', [str(src), str(dst)]);
con.close()"
artifacts/e2e/data/penguins.csv artifacts/e2e/data/penguins.parquet
- dataset_id: nyc_taxi_jan_2023
source_kind: parquet
source_url: https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet
provenance_url: https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page
sha256: 32df6f67578fa86c484a6b5ef23a5281992ff085521082340b0f9e5889e9a572
local_parquet_path: artifacts/e2e/data/yellow_tripdata_2023-01.parquet
fetch_command: >
curl -L https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet
-o artifacts/e2e/data/yellow_tripdata_2023-01.parquet
Scenario Definitions¶
s1_titanic_batch_core¶
scenario_id: s1_titanic_batch_core
goal: Cover the core eager-load and first-pass EDA flow in batch mode.
phases: [1, 2, 3]
mode: batch_c
dataset:
dataset_id: titanic
local_path: artifacts/e2e/data/titanic.parquet
preflight:
- uv sync
- mkdir -p artifacts/e2e/data artifacts/e2e/s1
- dataset fetch and parquet conversion must succeed
command:
argv:
- uv
- run
- tabdat
- -c
- use artifacts/e2e/data/titanic.parquet
- -c
- describe
- -c
- summarize age fare sibsp parch
- -c
- codebook sex class deck embarked survived
- -c
- count
- -c
- head 5
- -c
- tail 5
- -c
- keep if age >= 18
- -c
- drop deck alone
- -c
- rename sex gender
- -c
- generate family_size = sibsp + parch + 1
- -c
- replace fare = 0 if fare < 1
- -c
- select survived pclass class gender age fare family_size
- -c
- tabulate gender survived, row col missing
- -c
- by pclass: summarize age fare
- -c
- collapse mean age fare, by(survived)
- -c
- head 5
expected_exit_code: 0
expected_stdout_contains:
- "Loaded: artifacts/e2e/data/titanic.parquet"
- "Rows:"
- "Variable Type"
- "Variable Count Mean"
- "Nonmissing"
- "Distinct"
- "Examples"
- "Kept matching rows:"
- "Dropped selected columns:"
- "Renamed sex to gender:"
- "Generated family_size:"
- "Replaced fare:"
- "Selected columns:"
- "gender"
- "survived"
- "Row %"
- "Col %"
- "pclass"
- "Collapsed dataset:"
expected_stdout_regex:
- "Rows: [0-9]+"
- "Collapsed dataset: [0-9]+ rows, 3 columns"
expected_stderr: ""
expected_files: []
red_flags:
- any stderr output
- parse error around if clauses, by clauses, or comma options
- final preview still includes dropped columns
- generated family_size column absent after select
- collapse result does not reduce to survived plus aggregated columns
s2_interactive_shell_contract¶
scenario_id: s2_interactive_shell_contract
goal: Cover prompt-toolkit shell behavior that is observable through TTY interaction.
phases: [1, 3, 4, 5, 6]
mode: interactive_tty
dataset:
dataset_id: titanic
local_path: artifacts/e2e/data/titanic.parquet
preflight:
- uv sync
- dataset fetch and parquet conversion must succeed
- remove ~/.tabdat_history before the scenario if isolation is required
interactive_steps:
- send: "use artifacts/e2e/data/titanic.parquet\n"
- expect_stdout_contains: ["Loaded: artifacts/e2e/data/titanic.parquet"]
- send: "summarize a\t"
- expect_completion_contains: ["age"]
- send: "\u0003"
- send: "tabulate sex, \t"
- expect_buffer_contains: ["row", "col", "missing"]
- send: "\u0003"
- send: "by s\t"
- expect_completion_contains: ["sex"]
- send: "\u0003"
- send: "by sex: sum\t"
- expect_completion_contains: ["summarize"]
- send: "\u0003"
- send: "sql group\t"
- expect_completion_contains: ["group by"]
- send: "\u0003"
- send: "histogram a\t"
- expect_completion_contains: ["age"]
- send: "\u0003"
- send: "count\n"
- expect_stdout_regex: ["Rows: [0-9]+"]
- send: "exit\n"
history_reopen_check:
- reopen_shell: true
- send: "\u001b[A"
- expect_buffer_contains: ["count"]
- filesystem_check:
path: ~/.tabdat_history
must_exist: true
must_contain: ["use artifacts/e2e/data/titanic.parquet", "count"]
expected_exit_code: 0
expected_stderr: ""
expected_files:
- "~/.tabdat_history"
red_flags:
- empty column completion after dataset load
- options missing after a trailing comma
- by-child completion missing after colon
- history not persisted across sessions
- shell becomes unresponsive after completion or interrupt
notes:
- syntax coloring is not a pass/fail signal
- buffer checks require a TTY-aware harness, not plain stdout-only execution
s3_taxi_lazy_scale¶
scenario_id: s3_taxi_lazy_scale
goal: Cover the real lazy-load path on a larger public dataset, with SQL, plots, and persistence.
phases: [1, 3, 4, 6, 7, 9]
mode: batch_c
dataset:
dataset_id: nyc_taxi_jan_2023
local_path: artifacts/e2e/data/yellow_tripdata_2023-01.parquet
preflight:
- uv sync
- mkdir -p artifacts/e2e/data artifacts/e2e/s3
- dataset fetch must succeed
command:
argv:
- uv
- run
- tabdat
- -c
- use artifacts/e2e/data/yellow_tripdata_2023-01.parquet, lazy engine=duckdb
- -c
- describe
- -c
- count
- -c
- select passenger_count trip_distance fare_amount tip_amount total_amount payment_type
- -c
- keep if trip_distance >= 10
- -c
- drop if fare_amount == 0
- -c
- generate tip_share = tip_amount / total_amount
- -c
- summarize trip_distance fare_amount tip_amount total_amount tip_share
- -c
- sql select payment_type, avg(total_amount) as mean_total, avg(tip_share) as mean_tip_share from active group by payment_type order by payment_type
- -c
- histogram total_amount, bins=30 saving(artifacts/e2e/s3/total_amount_hist.svg) noopen
- -c
- bar payment_type, missing saving(artifacts/e2e/s3/payment_type_bar.svg) noopen
- -c
- save artifacts/e2e/s3/filtered_trips.parquet, replace
- -c
- sql select payment_type, count(*) as n from active group by payment_type order by payment_type into payment_summary
- -c
- head 10
expected_exit_code: 0
expected_stdout_contains:
- "Loaded: artifacts/e2e/data/yellow_tripdata_2023-01.parquet (unknown rows,"
- "lazy=duckdb"
- "Rows:"
- "Selected columns:"
- "Kept matching rows:"
- "Dropped matching rows:"
- "Generated tip_share:"
- "payment_type"
- "mean_total"
- "mean_tip_share"
- "Saved plot: artifacts/e2e/s3/total_amount_hist.svg"
- "Saved plot: artifacts/e2e/s3/payment_type_bar.svg"
- "Saved: artifacts/e2e/s3/filtered_trips.parquet"
- "Created payment_summary:"
expected_stdout_regex:
- "Rows: [0-9]+"
- "Saved: artifacts/e2e/s3/filtered_trips.parquet \\([0-9]+ rows, 7 columns\\)"
expected_stderr: ""
expected_files:
- artifacts/e2e/s3/total_amount_hist.svg
- artifacts/e2e/s3/payment_type_bar.svg
- artifacts/e2e/s3/filtered_trips.parquet
followup_probe:
argv:
- uv
- run
- tabdat
- -c
- use artifacts/e2e/data/yellow_tripdata_2023-01.parquet, lazy engine=polars
- -c
- count
- -c
- head 2
expected_exit_code: 0
expected_stdout_contains:
- "Loaded: artifacts/e2e/data/yellow_tripdata_2023-01.parquet (unknown rows,"
- "lazy=polars"
- "Rows:"
expected_stderr: ""
red_flags:
- initial lazy load prints a numeric row count instead of unknown rows
- batch mode attempts to auto-open plots
- save output is not Parquet-backed
- sql into does not replace the active dataset used by the final head
- polars selector is accepted but not reflected in the load banner
s4_penguins_script_repro¶
scenario_id: s4_penguins_script_repro
goal: Cover reproducible script execution, nested run, config loading, SQL multiline blocks, plots, and export.
phases: [1, 2, 3, 4, 6, 8, 9]
mode: script_file
dataset:
dataset_id: penguins
local_path: artifacts/e2e/data/penguins.parquet
preflight:
- uv sync
- mkdir -p artifacts/e2e/data artifacts/e2e/s4
- dataset fetch and parquet conversion must succeed
- write config and script files exactly as defined below
files_to_create:
- path: artifacts/e2e/s4/config.tabdat.toml
content: |
graph_format = "png"
artifact_dir = "artifacts/e2e/s4/artifacts"
graph_open = false
- path: artifacts/e2e/s4/prep.td
content: |
summarize bill_length_mm flipper_length_mm body_mass_g
generate body_mass_kg = body_mass_g / 1000
scatter body_mass_g flipper_length_mm, saving(artifacts/e2e/s4/artifacts/plots/penguins_scatter.png) noopen
- path: artifacts/e2e/s4/analysis.td
content: |
use artifacts/e2e/data/penguins.parquet
run prep.td
tabulate species sex, row col missing
sql """
select
species,
avg(body_mass_kg) as mean_body_mass_kg,
avg(flipper_length_mm) as mean_flipper_length_mm
from active
group by species
order by species
""" into penguin_summary
head 10
export artifacts/e2e/s4/penguin_summary.parquet, replace
command:
argv:
- uv
- run
- tabdat
- --config
- artifacts/e2e/s4/config.tabdat.toml
- -f
- artifacts/e2e/s4/analysis.td
expected_exit_code: 0
expected_stdout_contains:
- "Script: artifacts/e2e/s4/analysis.td"
- "TabDat:"
- "Python:"
- "Config: graph_format=png, artifact_dir=artifacts/e2e/s4/artifacts, graph_open=off"
- ". use artifacts/e2e/data/penguins.parquet"
- "Script: artifacts/e2e/s4/prep.td"
- ". summarize bill_length_mm flipper_length_mm body_mass_g"
- "Generated body_mass_kg:"
- "Saved plot: artifacts/e2e/s4/artifacts/plots/penguins_scatter.png"
- "species"
- "Row %"
- "Col %"
- "Created penguin_summary:"
- "Exported: artifacts/e2e/s4/penguin_summary.parquet"
expected_stdout_regex:
- "Exported: artifacts/e2e/s4/penguin_summary.parquet \\([0-9]+ rows, 3 columns\\)"
expected_stderr: ""
expected_files:
- artifacts/e2e/s4/config.tabdat.toml
- artifacts/e2e/s4/prep.td
- artifacts/e2e/s4/analysis.td
- artifacts/e2e/s4/artifacts/plots/penguins_scatter.png
- artifacts/e2e/s4/penguin_summary.parquet
red_flags:
- script metadata missing or reordered unexpectedly
- nested run fails to resolve relative paths
- plot auto-open occurs in script mode
- multiline sql block parse fails
- exported parquet is missing after a successful exit code
s5_titanic_phase13_dogfood¶
scenario_id: s5_titanic_phase13_dogfood
goal: Cover real-dataset Phase 13 dogfooding for regress/predict/estat in batch mode.
phases: [1, 3, 13]
mode: batch_c
dataset:
dataset_id: titanic
local_path: artifacts/e2e/data/titanic.parquet
preflight:
- uv sync
- mkdir -p artifacts/e2e/data artifacts/e2e/s5
- dataset fetch and parquet conversion must succeed
command:
argv:
- uv
- run
- tabdat
- -c
- use artifacts/e2e/data/titanic.parquet
- -c
- regress fare age
- -c
- predict fare_hat
- -c
- predict fare_resid, residuals
- -c
- estat residuals
- -c
- estat ovtest
- -c
- estat vif
- -c
- head 5
expected_exit_code: 0
expected_stdout_contains:
- "Loaded: artifacts/e2e/data/titanic.parquet"
- "Model: regress fare on age"
- "Estimator: ols"
- "Covariance: nonrobust"
- "Predicted fare_hat:"
- "Predicted fare_resid:"
- "Metric"
- "studentized_std_dev"
- "p_value"
- "Variable VIF"
- "age"
- "fare_hat"
- "fare_resid"
expected_stdout_regex:
- "Observations: [0-9]+"
- "Predicted fare_resid: [0-9]+ rows, [0-9]+ columns"
expected_stderr: ""
expected_files: []
red_flags:
- regress output header missing or malformed
- predict commands do not add generated columns to subsequent preview
- estat residuals/ovtest/vif fail after successful regress
- diagnostics output changes shape without a matching command-contract update
s6_canonical_parquet_workflow¶
scenario_id: s6_canonical_parquet_workflow
goal: Publish and benchmark one deterministic Parquet-first terminal EDA journey.
phases: [1, 2, 3, 7, 8, 9, 24]
mode: script_replay
dataset:
dataset_id: titanic
local_path: artifacts/e2e/data/titanic.parquet
script:
tracked_path: demos/canonical_parquet_eda.td
steps:
- lazy DuckDB Parquet load
- describe and count
- codebook missingness inspection
- adult-row filter and family-size derivation
- overall and by-class summaries
- class-level collapse
- Parquet export
replay:
runs: 2
compare_stdout: exact
compare_export_schema_and_rows: exact
benchmark:
measure: wall_clock_seconds_per_cli_run
threshold: none
expected_exit_code: 0
expected_stdout_contains:
- "lazy=duckdb"
- "Nonmissing"
- "Missing"
- "Generated family_size:"
- "Collapsed dataset: 3 rows, 4 columns"
- "Exported: artifacts/e2e/s6/canonical_summary.parquet (3 rows, 4 columns)"
expected_stderr: ""
expected_files:
- artifacts/e2e/s6/canonical_summary.parquet
red_flags:
- either replay emits stderr or a non-zero exit code
- replay transcripts differ for the same input and script
- exported schema or rows differ between replays
- timing is treated as a portable pass/fail threshold
Global Red Flags¶
global_red_flags:
- parser accepts a command that executor cannot honor without a clear user-facing error
- lazy-load output stops being honest about unknown initial row counts
- batch and script modes diverge on artifact naming or config behavior
- script transcripts become nondeterministic across repeated runs with the same inputs
- active-dataset replacement after collapse or sql into is not visible in subsequent commands