Reproducibility

Reproducible analysis means you can re-run exactly what you did — on the same data or on new data — and get the same result, and that someone else can read and verify your steps. ChakataStat is built around this: every menu action is also a command, and the commands form a stream you can read, edit, save, replay, batch and export.

The syntax console (.cks)

Open it with View → Syntax (Ctrl+4). It shows your session as an editable ChakataStat syntax script — the .cks language. Every analysis, chart and journalable transform you run from the menus appends a line here automatically:

descriptives variables=[Age, BMI, SystolicBP]
correlate variables=[BMI, SystolicBP], method=pearson
compute target=BMI_cat, expr="IF(BMI >= 30, 1, 0)"
histogram variable=BMI

The grammar is small and readable: toolname arg=value, arg=[a, b], arg="text", with # for comments. A saved script's first line is one such comment, # dataset <hash>, naming the data it was recorded against (see the dataset fingerprint).

One command per line

A command occupies exactly one line, however long it gets. There is no line-continuation character, and this is deliberate rather than unfinished: the journal's contract is one line per output, which is what lets the chart editor amend the line that produced a chart instead of appending a new one.

So if you paste a command that has been wrapped across two lines — out of a document, an email, or this guide before it was corrected — the second line is read as a new command and you get errors like Trailing "," with no argument and Unknown tool "colors". Both say the same thing: join the lines. The console names that cause when it can see it, and offers a Join wrapped lines button beside the errors that does it for you. The button appears only when joining actually makes the script parse, so it is never a guess — if it is not there, the problem is something else.

Long lines are normal here. The console wraps them for display; the stored script keeps them as one line.

The editor

The console is a real syntax editor, built so that a researcher who has never programmed can read it immediately — and so that editing never changes what a script means:

  • Line numbers in a gutter on the left. Long commands soft-wrap for display; the wrapped rows are not numbered, because they are still one line (and one command).
  • Syntax colours, muted and theme-derived in both light and dark themes: comments, tool names, argument names, strings and numbers each get a quiet distinction. Colouring is display-only — it never alters the text.
  • The current line carries a subtle full-width band, so the caret is easy to find in a long script.
  • Bracket matching: place the caret at a [ or ] and both ends of the list light up. A bracket inside a quoted string is text, not a bracket, and stays quiet.
  • Find & replace (Ctrl+F, or Ctrl+H with the replace row): a compact bar under the editor with a live match count, next/previous that wrap around, a match-case toggle (Aa), and highlighted matches with the current one emphasised. Replace changes the current match and moves on; All changes every match — and either way one Ctrl+Z takes it back in a single step. Esc closes the bar and clears the highlights.

The keyboard, at the editor:

Keys Action
Ctrl+Enter Run the selected lines — or the caret's line when nothing is selected
Ctrl+Shift+Enter Run the whole script
Ctrl+/ Comment or uncomment the selected lines
Ctrl+F Find (seeded from the selection, if any)
Ctrl+H Find & replace
Ctrl+Space Open the completion list at the caret
Ctrl+Z Undo — typing, a comment toggle, a replace-all, an accepted completion or a Join wrapped lines repair each undo as one step

(In the find bar, Enter and Shift+Enter step to the next and previous match, and Esc closes it. While the completion list is open, / move through it, Enter or Tab accepts, and Esc closes it — Tab goes back to moving focus the moment the list is closed. On keyboard layouts where / is not a plain key, the Ctrl+/ chord may not be available.)

As you type: checking, completion and hover help

The console checks your commands as you type. Shortly after you pause, each line is parsed — the same parse that would run it — and a broken line gets a wavy underline over its text and a dot in the gutter. Hover either one and the tooltip shows exactly what the parser will say if you press Run; for a misspelled tool or argument name it adds a suggestion when something in the catalog is close (Did you mean "variables"?). Fix the line and the marks disappear. Nothing is highlighted mid-word while you are still typing — the check waits for your pause, and the status panel under the editor reports the same errors when a run is blocked, so the two never disagree.

Completion offers what the command needs next, and only when it has something real to offer:

  • at the start of a line — the tool names, each with its one-line description;
  • after a tool — its remaining argument names, completed as name= (arguments the line already has are not offered again);
  • after the = of an argument that takes variables, or inside its [...] list — your dataset's variables, each with its type, measurement level and label;
  • for an option with a fixed set of values (like method=) — the allowed values.

The list opens as you type a name (or on Ctrl+Space anywhere), narrows with every keystroke, and never inserts anything you did not accept: Enter, Tab or a click takes the highlighted candidate, Esc (or just clicking elsewhere) makes it go away. Accepting replaces the whole half-typed word, and one Ctrl+Z undoes it.

Hover help is the same knowledge at rest: hold the pointer over a tool name to see what it does, over an argument name for what it accepts, or over a variable in a command to see its type, measurement level and label. It reads only what is already known — nothing is computed or analyzed by pointing at things.

Running and editing

  • Run all / Run selection parse the script and dispatch each command into the Output log. Replaying does not re-record the commands.
  • Parse errors and failed commands are reported in a status panel as data — a bad line never crashes the run. Drag the panel's top edge to make it taller (double-click the edge to reset); its text follows the Content text size setting.
  • The console and the GUI are two views of one stream: edit the text and re-run, or keep clicking menus and watch the script grow.

Options a dialog does not expose

Every command is a tool name plus named arguments, and the console accepts arguments the dialog has no control for. Two groups are worth knowing about:

Chart presentation. title and colors (a list of hex strings, one per series, in series order) are accepted by every chart tool; legend (auto / right / bottom / none) by the charts that draw one; line_width by the charts that draw lines; and stacked / stack_mode / bar_gap by the bar chart. They are journaled, so a styled chart re-runs styled:

kaplan_meier time=Months, status=Event, factor=Treatment, plot=true, legend=bottom, colors=["#1f77b4", "#d62728"], line_width=2.5

This is also the per-chart override for the palette chosen in Settings — use it for a one-off (matching a house style in a report) without changing every other chart.

You rarely have to type any of it: the chart editor sets the same arguments from a preview, and writes them back into this same line.

Randomisation seeds. Anything that draws random numbers takes a seed, and defaults to 1 so that a command is reproducible unless you say otherwise — the bootstrap, multiple imputation, the Monte-Carlo permutation tests, and the quantile residuals behind a logistic or count model's diagnostic plots. Change it to check that a result is not an artefact of one particular draw:

logistic_regression dependent=Hypertensive, predictors=[Age, BMI], diagnostics=true, seed=7

When editing a chart amends the script

The chart editor is the one thing that writes into the middle of your script rather than appending to it. Applying an edit finds the line that produced the chart, replaces it with the same command carrying the style you chose, and swaps the restyled chart into the same place in the Output log — same position, same timestamp. If you ran the same command twice, each chart tracks its own line, so editing the second one leaves the first alone.

If that line is not there any more — you rewrote or deleted it, or the chart came from the AI assistant, which runs tools without recording them — the styled command is appended instead, and the chart's note says so. Nothing is guessed at, and either way the script still reproduces the output you are looking at, styled as you see it.

Saving and opening scripts

Save the script as an .cks file and open it later (or hand it to a colleague). The session also remembers the script between launches.

The command palette

View → Command Palette… (Ctrl+Shift+P) is a fast, keyboard-driven launcher: start typing the name of any analysis, chart, transform, file or view command and run it without hunting through menus. It is built from the same tool catalog the menus, the AI and MCP use, so it can never drift out of sync.

Direct dispatch

The palette also speaks .cks. Type a fully-specified command — the same one-line grammar as the syntax console:

frequencies variables=[Age, BMI]

and a Run row appears at the top. Enter dispatches the call straight through the engine, bypassing the dialog, and journals exactly the line the dialog would have journaled — so a palette-run analysis replays like any other. This is for the moment you already know the call you want: the same user who edits the console should not have to re-enter it through a form.

  • A partially-specified call (required arguments missing) does not error: the row offers to open the tool's dialog instead, and names what is missing.
  • A call that fails (a mistyped variable, a bad argument value) shows the engine's message in the palette, with your text still editable.
  • A plain word never triggers any of this — the fuzzy search only yields to the parser once the query contains an =.

Headless batch mode (--run)

For pipelines, CI and reproducible research, run a script against a dataset with no window:

ChakataStat --run script.cks data.ckd --out report.html

This loads the dataset, runs every command in the .cks script through the same engine, and writes the output log as a report. The output format follows the --out extension — HTML, Markdown or LaTeX. It shares the headless engine bootstrap with the MCP mode, so batch results match what the GUI produces.

A third headless mode, --verify, runs the built-in installation check and exits 0 only on a full pass — see Verify this Installation. Together they close the loop: --verify proves the installed engine reproduces independent reference values, and --run replays your own script on it.

The dataset fingerprint — "same data"

Every result's footer carries, beside the engine version, a short data fingerprint: data 3f2a9c1b7e04. It is the first twelve characters of a SHA-256 over the data your dataset holds — every value, in column order, with each variable's type and your user-missing definitions applied — so two results with the same fingerprint were computed from the same numbers. The footer also names the active conditions: filter age > 30, weighted by w, split by group.

The fingerprint deliberately ignores anything that cannot change a result: variable names, labels, value labels, measurement levels, column widths. Rename or relabel as much as you like — the fingerprint stands. Edit a value, change a type, or declare a missing code, and it moves. The same data opened from a .ckd file, a CSV or a Parquet file has one fingerprint.

On a saved script. When you Save Syntax…, the .cks file starts with a comment naming the data it was recorded against:

# dataset 3f2a9c1b7e04a1e5…  (the full 64-character hash)
descriptives variables=[Age, BMI]

It is an ordinary comment — older versions read the script unchanged, and you can delete it. When you open that script later and run it — in the console, or headless with --run — ChakataStat compares the header with the dataset that is open before the first command runs, and puts the verdict at the top of the output as a Script Provenance entry:

  • Same data: yes — the open dataset is, value for value, what the script was recorded against.
  • NO — different data — a warning, with both hashes. The script still runs; the results are for the data you have open, and the entry says so. Nothing refuses to run on your behalf here: that is the job of the reproducibility bundle (a later feature), whose whole point is to refuse.

The full convention — exactly what is hashed and how — is recorded in the developer documentation, so a reader can compute the same hash independently.

The reproducibility bundle (.cksb)

A bundle is one small file that lets someone else check your results — re-run this and compare — without you having to describe what you did.

As the author. With your analysis in the output log, choose File → Export Reproducibility Bundle…. The .cksb file contains:

  • your syntax journal (the .cks script);
  • the data fingerprint (and the name of the file you had open);
  • the ChakataStat version, the engine version and your platform;
  • every output in the log, exactly as it was rendered — every table, and each chart as the geometry table its CSV export produces.

It does not contain your data. Whether and how to share the data is your decision, made separately; the bundle only says what you ran and what you saw. For a clean bundle, Clear All the output and Run All from the console first, so the log holds exactly what the journal produces (an assistant reply or an installation report in the log has no journal line to re-run, and the verifier will say so).

From a pipeline, --run script.cks data.ckd --bundle analysis.cksb writes the same bundle from a headless run.

As the reader. Obtain the data from the author, then either File → Verify Reproducibility Bundle… (pick the bundle, then the data file), or headless:

ChakataStat --verify analysis.cksb data.csv --out report.html

The verifier checks the data first. If its fingerprint is not the bundle's, it refuses — the report says REFUSED — different data with both hashes, and nothing is re-run. This is stricter than the script header's warning on purpose: the bundle's whole point is to compare against the author's data. With the right data it re-runs the journal and compares every cell of every output with the bundle, within the precision the author saw it printed at, and reports PASS or FAIL per output, naming the first cells that differ. The exit status is 0 only on a full pass, so a reviewer's script can gate on it.

Where the installation check (Verify this Installation) proves the software on a machine, the bundle proves a particular result — together with the fingerprint they are the three questions a sceptical reader asks, each answered by a report rather than a promise.

Exporting the output log

File → Export Output… (or the Output view's Export… button) writes your whole results log to HTML, Markdown or LaTeX — the format chosen from the file extension. Tables are escaped per format and charts are rendered as their geometry. For a single result, right-click its card and choose Export as CSV… to drop it straight into a spreadsheet (or use copy-to-clipboard).

Exporting a session as Python

Export as Python… (from the syntax console) renders your recorded session as a runnable Python script for the ChakataStat Python client — installable from PyPI:

pip install chakatastat

The client drives a headless engine over MCP and exposes every analysis as a method; it needs the ChakataStat app installed (it contains no statistics of its own), and its version tracks the app's, so install the client version matching your app:

import chakatastat as ig

ds = ig.open("health_study.ckd")     # spawns the headless engine
ds.descriptives(["Age", "BMI", "SystolicBP"])
ds.regression("SystolicBP", ["Age", "BMI"])

for col in ds.numeric_vars():         # real Python: loops, logic
    ds.frequencies([col])

df = ds.to_polars()                   # escape hatch into the Polars ecosystem

This gives you full programmatic control and the whole Python data ecosystem (polars, numpy, scipy, statsmodels) without ChakataStat embedding any of it — Python is an external client over the same seam the AI and MCP use, so the single engine and the errors-as-data discipline are preserved.

Why this matters

The flat command stream is the one canonical record of an analysis. Because the GUI both emits it and can reconstruct itself from it, "what I clicked" and "what I can replay" are the same thing — the foundation that the batch mode, the command palette, user-defined tools and the Python export all build on.

See also