pi-eda 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # pi-eda
2
+
3
+ Pi skills and subagents for interactive exploratory data analysis.
4
+
5
+ - `skills/exploring-data`: iterative analysis & plotting
6
+ - `skills/finishing-exploratory-data-analysis`: locking in analysis into a wiki-style report
7
+ - `show` extension: renders a figure to the user in the TUI without spending tokens on it
8
+
9
+ ## Requires
10
+
11
+ The [`pi-subagents`](https://www.npmjs.com/package/pi-subagents) package, for
12
+ custom-agent support and the `/skill:pi-subagents` dispatch pattern the skills
13
+ reference.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pi install git:github.com/shivam-v-verma/pi-eda
19
+ pi install npm:pi-eda
20
+ ```
21
+
22
+ ## Matplotlib style
23
+
24
+ `skill:exploring-data` respects a matplotlib template `eda.mplstyle`,
25
+ usually located at `~/.config/matplotlib/stylelib/eda.mplstyle`.
26
+
27
+ A minimal example file `eda.mplstyle.example` is given here.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: critical-auditor
3
+ description: Confirms an EDA report/script's claims match the agreed intent, not just clarity
4
+ tools: read, grep, find
5
+ defaultContext: fork
6
+ ---
7
+
8
+ Confirm the report's claims and the script's implementation actually
9
+ match what was agreed -- correct semantic intent, not just clarity. If
10
+ you have no conversation context (fork wasn't available), expect a
11
+ written recap of the framing/decisions instead -- context-free, you're
12
+ a naive reader and thus not able to do your job properly.
13
+
14
+ - Inputs: the report, figure(s), numbered script(s), and any associated
15
+ summary data. You need all of the work product to evaluate correctness.
16
+ - Check whether the report/script match what was agreed; flag drift
17
+ between intent and implementation.
18
+ - Distrust rule: don't trust matching/ordering/canonicalization logic
19
+ verified only on one easy example -- check it was tested on nontrivial
20
+ cases that could actually fail.
21
+ - Findings have no tiers: everything you report is drift between intent
22
+ and implementation. Never silently resolve it yourself -- report it
23
+ verbatim for the calling agent to surface.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: job-runner
3
+ description: Launches and monitors a heavy/long-running analysis script in the background
4
+ tools: bash, read
5
+ inheritProjectContext: true
6
+ ---
7
+
8
+ You own execution and monitoring of a full-scale run that's already been
9
+ checked and cleared to launch. You do not review or edit the script --
10
+ that already happened before you were dispatched.
11
+
12
+ ## What to do
13
+
14
+ - Confirm inputs before launching:
15
+ - The estimated runtime and peak memory from the smoke test (should be
16
+ in the task you were given -- if missing, ask the dispatching agent
17
+ for it rather than guessing).
18
+ - Whether `systemd-run` is available on this machine.
19
+ - Launch the script as a background process (following the project's run
20
+ convention, e.g. `uv run`).
21
+ - Cap it with a memory limit so a runaway process gets killed by the OS
22
+ instead of taking down the machine, sized off the estimate with
23
+ headroom (e.g. 2x the estimated peak):
24
+ - `systemd-run --scope -p MemoryMax=<budget> --user -- uv run <script>`
25
+ if available.
26
+ - Otherwise, say so explicitly and fall back to periodic `ps`/`smem`
27
+ checks with a manual kill.
28
+ - Monitor progress and memory use periodically rather than blocking on it
29
+ synchronously -- this is in addition to the hard cap above, not a
30
+ replacement for it, since you want to know about a job trending toward
31
+ the ceiling before it gets killed.
32
+ - Report back once it completes: success/failure, runtime, and where the
33
+ output landed. On failure, include the actual error, not just "it
34
+ failed."
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: naive-reader
3
+ description: Confirms an EDA report reads as a clear, complete, standalone artifact
4
+ tools: read
5
+ defaultContext: fresh
6
+ ---
7
+
8
+ Confirm the report (with `index.md`) reads as a clear, comprehensive,
9
+ standalone artifact -- no missing context, no unexplained gaps. Read it
10
+ cold, as if returning in 6 months having forgotten everything.
11
+
12
+ - Inputs: report file, figure(s), summary CSV(s), and `index.md` for
13
+ project context. You may open the script only to resolve a specific
14
+ confusion, never to run it.
15
+ - Comprehension only -- you never judge whether a choice was right, only
16
+ whether it's explained.
17
+ - Confirm every figure file passed to you is actually embedded in the
18
+ report -- an unreferenced figure is material-tier, since there isn't an
19
+ interpretation for it.
20
+
21
+ ## Severity rubric
22
+
23
+ | Tier | Definition |
24
+ | --- | --- |
25
+ | cosmetic | wording/labeling nit, doesn't change interpretation |
26
+ | confusing | correct and complete, but too dense/jargon-heavy to parse easily |
27
+ | clarifying | you can't tell why/what/how from the report alone. If you had to open the script to answer your own question, that's automatic clarifying-tier |
28
+ | material | your conclusion contradicts what the report claims, or the claim isn't backed by anything shown |
29
+
30
+ Report every finding with its tier. Do not silently decide a fix for
31
+ anything -- that's the calling agent's call, not yours.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: script-checker
3
+ description: Checks a script against stated intent/framing before a heavy scale-up run
4
+ tools: read, grep, find
5
+ inheritProjectContext: true
6
+ ---
7
+
8
+ You check a script against the user's stated intent/framing before an
9
+ expensive full-scale run. You review; you do not edit the script or run
10
+ it.
11
+
12
+ ## What to check
13
+
14
+ - Does each transform (groupby, join, pivot, filter, etc.) actually do
15
+ what the stated intent/framing says it should.
16
+ - Any matching/ordering/canonicalization logic verified only on an
17
+ easy/symmetric case -- that's a red flag, not a pass. Ask whether it
18
+ was tested against a case that could actually fail.
19
+ - Anything that would silently produce a plausible-but-wrong result
20
+ (wrong join key, off-by-one window, unintended dtype coercion).
21
+
22
+ ## Output
23
+
24
+ - If everything checks out, say so plainly -- don't invent issues.
25
+ - If something's off, name the exact line/transform and what's wrong
26
+ with it, specific enough that the calling agent can fix it without
27
+ guessing.
@@ -0,0 +1,28 @@
1
+ # Shared matplotlib style for /skill:exploring-data.
2
+ # This is a template shown for example; include in default matplotlib
3
+ # style sheet dir (see README.md).
4
+ # See plotting.md for the conventions this backs and what still needs a
5
+ # per-script judgment call (aspect ratio, bin count, title text, etc).
6
+ #
7
+ # Full rcParams menu:
8
+ # https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/mpl-data/matplotlibrc
9
+
10
+ figure.dpi: 150
11
+ savefig.dpi: 150
12
+ savefig.format: png
13
+ savefig.bbox: tight
14
+ figure.autolayout: True
15
+
16
+ axes.grid: False
17
+ # CSS named colors: https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
18
+ axes.prop_cycle: cycler('color', ['lightsteelblue', 'darksalmon', 'darkseagreen', 'plum', 'burlywood', 'rosybrown'])
19
+
20
+ font.size: 10
21
+ axes.titlesize: 10
22
+ axes.labelsize: 10
23
+ legend.fontsize: 10
24
+ xtick.labelsize: 10
25
+ ytick.labelsize: 10
26
+ legend.frameon: False
27
+
28
+ image.cmap: Blues
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Show Extension - Render an image to the user without spending LLM tokens on it.
3
+ *
4
+ * `show` reads an image file and renders it in the TUI via the built-in
5
+ * `Image` component. The LLM only ever sees a short confirmation string in
6
+ * `content` (what gets sent back to the model) - the image bytes live in
7
+ * `details`, which is TUI-only and never enters LLM context. If the model
8
+ * later needs to actually look at the image, it uses the normal `read` tool.
9
+ */
10
+
11
+ import { readFile, stat } from "node:fs/promises";
12
+ import { extname, resolve } from "node:path";
13
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
14
+ import { Image, Text } from "@earendil-works/pi-tui";
15
+ import { Type } from "typebox";
16
+
17
+ const MEDIA_TYPES: Record<string, string> = {
18
+ ".png": "image/png",
19
+ ".jpg": "image/jpeg",
20
+ ".jpeg": "image/jpeg",
21
+ ".gif": "image/gif",
22
+ ".webp": "image/webp",
23
+ ".bmp": "image/bmp",
24
+ };
25
+
26
+ interface ShowDetails {
27
+ path: string;
28
+ base64: string;
29
+ mediaType: string;
30
+ }
31
+
32
+ export default function (pi: ExtensionAPI) {
33
+ pi.registerTool({
34
+ name: "show",
35
+ label: "Show",
36
+ description:
37
+ "Display an image to the user in the terminal without loading its pixels into your own context. " +
38
+ "Use read on the same path afterward if you need to analyze the image yourself.",
39
+ promptSnippet:
40
+ "Show an image to the user in the terminal (does not add the image to your context)",
41
+ promptGuidelines: [
42
+ "Use show to display an image the user asked to see; use read instead when you need to look at the image yourself.",
43
+ ],
44
+ parameters: Type.Object({
45
+ path: Type.String({
46
+ description: "Path to the image file (png, jpg, gif, webp, bmp)",
47
+ }),
48
+ }),
49
+
50
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
51
+ const rawPath = params.path.startsWith("@")
52
+ ? params.path.slice(1)
53
+ : params.path;
54
+ const absolutePath = resolve(ctx.cwd, rawPath);
55
+
56
+ const mediaType = MEDIA_TYPES[extname(absolutePath).toLowerCase()];
57
+ if (!mediaType) {
58
+ return {
59
+ content: [
60
+ { type: "text", text: `Not a supported image type: ${params.path}` },
61
+ ],
62
+ isError: true,
63
+ };
64
+ }
65
+
66
+ const fileStat = await stat(absolutePath).catch(() => null);
67
+ if (!fileStat?.isFile()) {
68
+ return {
69
+ content: [{ type: "text", text: `File not found: ${params.path}` }],
70
+ isError: true,
71
+ };
72
+ }
73
+
74
+ if (ctx.mode !== "tui") {
75
+ return {
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: `Cannot render images outside interactive TUI mode (${ctx.mode}).`,
80
+ },
81
+ ],
82
+ isError: true,
83
+ };
84
+ }
85
+
86
+ const base64 = (await readFile(absolutePath)).toString("base64");
87
+
88
+ return {
89
+ content: [{ type: "text", text: `Displayed ${params.path} to the user.` }],
90
+ details: { path: params.path, base64, mediaType } as ShowDetails,
91
+ };
92
+ },
93
+
94
+ renderCall(args, theme, _context) {
95
+ return new Text(
96
+ theme.fg("toolTitle", theme.bold("show ")) + theme.fg("muted", args.path),
97
+ 0,
98
+ 0,
99
+ );
100
+ },
101
+
102
+ renderResult(result, _options, theme, _context) {
103
+ const details = result.details as ShowDetails | undefined;
104
+ if (!details) {
105
+ const text = result.content[0];
106
+ return new Text(text?.type === "text" ? text.text : "", 0, 0);
107
+ }
108
+ return new Image(details.base64, details.mediaType, theme, {
109
+ maxWidthCells: 80,
110
+ maxHeightCells: 24,
111
+ });
112
+ },
113
+ });
114
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "pi-eda",
3
+ "version": "0.1.0",
4
+ "description": "Pi tooling for interactive exploratory data analysis.",
5
+ "license": "MIT",
6
+ "author": "Shivam V Verma",
7
+ "keywords": ["pi-package", "pi", "eda", "exploratory-data-analysis"],
8
+ "files": [
9
+ "README.md",
10
+ "agents",
11
+ "eda.mplstyle.example",
12
+ "extensions",
13
+ "skills"
14
+ ],
15
+ "peerDependencies": {
16
+ "@earendil-works/pi-coding-agent": "*",
17
+ "@earendil-works/pi-tui": "*",
18
+ "typebox": "*"
19
+ },
20
+ "pi": {
21
+ "extensions": ["./extensions"],
22
+ "skills": ["./skills"],
23
+ "subagents": {
24
+ "agents": ["./agents"]
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: exploring-data
3
+ description: Use when exploring a dataset, generating plots, or iterating on data analysis scripts
4
+ ---
5
+
6
+ > **Related skills:** Dispatch the `script-checker` with `/skill:pi-subagents`. Once a figure/analysis seems meaningful, use `/skill:finishing-exploratory-data-analysis` before writing it up or committing.
7
+
8
+ # Exploring Data
9
+
10
+ **Scope, small to large:**
11
+
12
+ - *plot*: a chart type/category (e.g. histogram, scatter)
13
+ - *figure*: one rendered image
14
+ - *question*: one concept to test through one or more figures
15
+ - *analysis*: one `.md` *report* file + its paired scripts/figs folder
16
+ - *project*: the whole `eda/` directory, created across many
17
+ `/skill:exploring-data` sessions
18
+
19
+ ## Overview
20
+
21
+ **Core principle:** Your job is to help the user surface hypotheses,
22
+ and suggest or build the plots that interrogate them. What a confirmed pattern
23
+ *means*, the story it tells, is the user's call: hand back a clear summary of
24
+ what the figure shows, not a conclusion dressed as a finding.
25
+
26
+ See `workflow.md` for stages of this skill. In all stages, confirm framing
27
+ with the user. Never run a batch of steps by yourself; always interleave
28
+ by checking for the user's insight/thoughts.
29
+
30
+ A good hypothesis is usually a simple, clean one -- reachable through better
31
+ framing of the question, not through aggressively slicing the dataset more ways
32
+ looking for a signal.
33
+
34
+ ## Directory Structure
35
+
36
+ Default to `eda/` at the project root (ask if a different name/location is
37
+ wanted). See `directory-structure.md` for the full layout convention
38
+ (scripts/figs tree, numbering rules, shared-code placement).
39
+
40
+ ## Core Data Practices
41
+
42
+ Always, whether running a numbered script or an ad hoc check (e.g.
43
+ `python -c "..."`):
44
+
45
+ - Check input file size(s) and be aware of runtime/memory.
46
+ - Set a timeout via the `timeout` param (seconds) in pi's `bash` tool
47
+ on any check or smoke test. If it times out, reduce sample size and
48
+ extrapolate rather than waiting for a highly accurate estimate.
49
+ - Don't delete scripts or attempts yourself unless the user explicitly
50
+ asks to. If a script is paused or abandoned, leave a module/function
51
+ docstring giving its status and where it was left off.
52
+ - If processing is heavy, decide with the user whether to run on the
53
+ full dataset or a representative subset for this pass.
54
+
55
+ ## Common Mistakes
56
+
57
+ The most cross-cutting/devastating failures. Each reference file above has
58
+ its own narrower table.
59
+
60
+ | Mistake | Fix |
61
+ | --- | --- |
62
+ | Running the full dataset before a smoke test | See `checking-and-running-scripts.md` -- always sample/time on a subset first |
63
+ | Auto-advancing to the next question without a framing check | See `pre-code-checkpoints.md` -- confirm framing before each new question, not once for the whole analysis |
64
+ | Picking the report/subfolder name yourself instead of asking | Ask before creating it -- one name, shared by the file and its folder |
65
+ | Launching a heavy scale-up run while the `script-checker` is still pending | See `checking-and-running-scripts.md` -- wait for it, or the expensive run finishes before a material issue could surface |
66
+ | Deleting/pruning a failed or paused script | Leave it in place with a status docstring unless the user explicitly asks to remove it |
67
+ | Treating an approval-sounding signal ("looks great", "keep going", "I trust your judgment") as done | Ask whether to move to `/skill:finishing-exploratory-data-analysis` -- don't auto-advance and don't stay silent |
68
+ | Chasing a pattern that only appears after trying many slices/cuts of the data | Multiple-comparisons risk -- go back to framing, not more cuts |
69
+ | Stating a small or non-random-sample result as a general fact | State it as a property of the sample examined ("true in this subset") until checked for generalization |
70
+ | Writing to `index.md` or the report before `/skill:finishing-exploratory-data-analysis` | Both are created/updated together at lock-in checkpoint -- confirm scope/framing conversationally during exploration instead |
@@ -0,0 +1,97 @@
1
+ # Checking and Running Scripts
2
+
3
+ Building confidence in a script for `/skill:exploring-data`, then deciding
4
+ how to run it. See `workflow.md` for where this fits in the full loop
5
+ (the light/heavy fork is inlined there, each branch with its own check
6
+ step and loop-back on findings).
7
+
8
+ ## Sanity Checking a Script
9
+
10
+ - After each nontrivial transform (groupby, join, pivot, filter, etc.), do
11
+ a quick check (`.head()`, row counts, spot-check a few values) that the
12
+ output matches what the transform should have done -- not just once at
13
+ the end of the script.
14
+ - Pick a case that could actually fail, not the first one at hand --
15
+ especially for canonicalization/ordering/matching logic, where an easy
16
+ case can pass while hiding a real bug.
17
+
18
+ ## Smoke Testing a Script
19
+
20
+ - Run on a sampled/head subset (fixed seed) and time it before running at
21
+ full scale.
22
+ - After it runs, glance at the actual output values, not just "it
23
+ completed in time." A fast clean run on garbage output is not a
24
+ passing smoke test.
25
+ - Exception: a cached-read-and-plot script -- one that purely reads an
26
+ already-validated cached table and plots it, with zero computation (no
27
+ groupby, join, pivot, or filter). State that justification explicitly
28
+ instead of skipping silently.
29
+
30
+ ## Judging Heavy vs. Light Scale-up
31
+
32
+ Goal: increase iteration speed without being reckless. Either extreme
33
+ wastes the user's time -- blocking every quick diff on a check likely to
34
+ pass, or committing to a large/expensive run that turns out wrong.
35
+
36
+ - Judge from the smoke-test timing/size ratio whether the full-scale run
37
+ will be heavy (long-running or memory-heavy relative to the sample --
38
+ e.g. 1% took a second but full scale extrapolates to minutes).
39
+ - Rough thresholds based on estimated time: light if under ~1 minute,
40
+ heavy if over ~5 minutes. In between is a judgment call.
41
+ - State that judgment explicitly: "sample took Xs, full run estimated ~Y,
42
+ treating as heavy."
43
+
44
+ ## Light Scale-up
45
+
46
+ - No `script-checker` dispatch -- reread the script yourself before
47
+ running: does it match the user's stated intent/framing, does each
48
+ nontrivial transform do what it should.
49
+ - If the reread surfaces an issue, fix it and reread again before
50
+ running -- don't run first and raise the concern after.
51
+ - This is a deliberately lower bar than the heavy path: a light-path
52
+ mistake is cheap to catch downstream too, since the run finishes fast
53
+ and you're looking at the output right away. The real second-set-of-
54
+ eyes gate for the analysis still happens at
55
+ `/skill:finishing-exploratory-data-analysis`'s lock-in checkpoint --
56
+ this reread isn't the last line of defense, it's a cheap intermediate
57
+ one.
58
+
59
+ ## Heavy Scale-up
60
+
61
+ - Dispatch the `script-checker` before launching:
62
+ `subagent({agent:"script-checker", task:"<script path + stated intent/framing>"})`.
63
+ - Do not launch until it has returned and come back clean.
64
+ - If it's still running, wait for it (or say explicitly you're waiting)
65
+ -- don't launch anyway planning to fold in feedback once it lands.
66
+ - Launching "in parallel while the `script-checker` runs in the
67
+ background" still counts as launching without it -- the expensive run
68
+ finishes and gets shown to the user before a material issue could
69
+ surface.
70
+ - A `script-checker` pass only checks the script -- it never substitutes
71
+ for the lock-in checkpoint in
72
+ `/skill:finishing-exploratory-data-analysis` once a figure/question is
73
+ ready to show the user.
74
+ - Two or more edits since the last `script-checker` dispatch count as one
75
+ nontrivial change on their own -- dispatch again at that point, don't
76
+ wait for it to feel big enough.
77
+
78
+ ### Dispatching the `job-runner`
79
+
80
+ - Once clear to launch:
81
+ `subagent({agent:"job-runner", task:"<script path + output location + estimated runtime + estimated peak memory>"})`.
82
+ This frees the main session to keep iterating while it runs. Pass the
83
+ runtime/memory estimate from your smoke test (see "Judging Heavy vs.
84
+ Light Scale-up" above) rather than making it re-derive them blind.
85
+
86
+ ## Common Mistakes
87
+
88
+ | Mistake | Fix |
89
+ | --- | --- |
90
+ | Running the full dataset (or an ad hoc exploratory probe) before a smoke test | Always sample/time on a subset first -- applies to one-off probes too, not just numbered scripts |
91
+ | Silently skipping the smoke test on a "just read cache and plot" script | Only skip for zero-computation cached-read-and-plot, and state that justification explicitly |
92
+ | Trusting a sanity check that only ran on an easy/symmetric case | Pick a case that could actually have failed, especially for matching/ordering/canonicalization logic |
93
+ | Checking that a smoke test ran fast/clean without looking at what it produced | Glance at the actual output values -- a clean run on garbage isn't a pass |
94
+ | Running a light-path script without rereading it first because it's "just quick" | Reread against the stated intent/framing before running -- speed doesn't waive the check, it just changes who does it |
95
+ | Launching a heavy scale-up "while the `script-checker` runs in the background" instead of waiting for it | Confirm the `script-checker` came back clean before launching -- don't launch and plan to fold feedback in later |
96
+ | Letting 2+ small edits pile up on a heavy-path script since the last `script-checker` dispatch because none felt big enough alone | Dispatch again once 2+ edits have landed |
97
+ | Treating a mid-work `script-checker` pass as covering the finishing skill's lock-in checkpoint | Different gate, different artifact -- a pending or completed `script-checker` pass never substitutes for it |
@@ -0,0 +1,44 @@
1
+ # Directory Structure
2
+
3
+ Layout convention for `/skill:exploring-data`.
4
+
5
+ Default to `eda/` at the project root (ask if a different name/location is
6
+ wanted).
7
+
8
+ ```text
9
+ eda/
10
+ index.md # landing pad for readers -- what's been done,
11
+ # links to each report below
12
+ customer_churn.md # a report -- lives directly under eda/
13
+ customer_churn/ # paired folder, same stem as the report file
14
+ scripts/
15
+ common_data.py # unnumbered: shared code, not an analysis step
16
+ 01_description.py
17
+ 02_description.py
18
+ figs/
19
+ 01_description.png
20
+ 01_description_b.png # one script can produce many figures
21
+ 02_description.png
22
+ ```
23
+
24
+ - `index.md` is a skimmable document for first-time readers, briefly
25
+ describing what analyses have been done, with links to the reports.
26
+ - A report (`<name>.md`) is the analysis's confirmed write-up -- its
27
+ questions, each with approach/results/figures, and a conclusion.
28
+ - The report file and its folder must share the exact same stem -- that's
29
+ the only thing that pairs them, no manifest or frontmatter needed.
30
+ - `index.md` and the report are only created/updated during
31
+ `/skill:finishing-exploratory-data-analysis`, and not during exploration.
32
+ - No strict 1:1 between scripts, figures, and a report's *Questions* -- number for
33
+ correlation, don't enforce it.
34
+ - Numbered scripts (`NN_description.py`) can't import each other.
35
+ - Shared code (data loading, plotting helpers, constants) goes in an
36
+ unnumbered `common_<topic>.py` and gets imported from there instead --
37
+ never import one numbered script from another.
38
+
39
+ ## Common Mistakes
40
+
41
+ | Mistake | Fix |
42
+ | --- | --- |
43
+ | Naming the report file and its folder with different stems | They must match exactly -- that's the only thing that pairs them |
44
+ | Importing one numbered script from another | Shared code goes in `common_<topic>.py` instead |
@@ -0,0 +1,74 @@
1
+ # Plotting Preferences
2
+
3
+ Style conventions and the figure-production process for
4
+ `/skill:exploring-data`. Defaults below apply to every plot
5
+ type; subsections add rules for a specific type. If a plot type has no
6
+ subsection yet, follow Defaults and ask if unsure -- don't invent a
7
+ per-type rule that hasn't been stated.
8
+
9
+ ## Process
10
+
11
+ - Generate figures with matplotlib, saved as PNG, following the conventions
12
+ below.
13
+ - Apply the user's `eda.mplstyle` at the top of the script
14
+ (`plt.style.use("eda")`) and respect user defaults.
15
+ - Visually inspect every figure with the `read` tool before treating it as a
16
+ result -- never make hypotheses from a figure you haven't looked at.
17
+ - `read` is for your own inspection. When you're ready to show the figure to
18
+ the user (the "Summarize the answer" handoff), use `show` to display it.
19
+ This will render the PNG to the user without spending your tokens
20
+ rereading it.
21
+ - Check specifically for: overlapping/clipped labels, indistinguishable
22
+ traces/series, and rendering artifacts (banding, gaps) that don't reflect
23
+ real structure in the data.
24
+ - Save a table alongside the figure if it's built from a compact aggregate
25
+ (roughly <=20 distinct values) rather than a full-row plot -- cheap at that
26
+ size, and it avoids guessing what the report will cite.
27
+
28
+ ## Defaults
29
+
30
+ If the user has not set up `eda.mplstyle` or has not offered opinions
31
+ on plot style, use these defaults:
32
+
33
+ - Colors: `eda.mplstyle` (see the repo-root `eda.mplstyle.example`) defaults to
34
+ `lightsteelblue` for one color and `Blues`
35
+ for a colormap -- pick something else only if needed to convey a point, e.g.
36
+ categorical comparison.
37
+ - Gridlines: none. Spines / ticks: matplotlib defaults -- don't add or strip
38
+ either.
39
+ - Titles: Be brief -- words must earn their space. Detailed descriptions belong
40
+ in report prose, not the title.
41
+ - Axis labels and legends: always present, be terse.
42
+ - Sample size: prefer density/percentage over raw counts unless there's a
43
+ specific reason to show counts.
44
+
45
+ ## Bar-shaped Plots (Bar Charts, Histograms)
46
+
47
+ - Aspect ratio: 8:3 (wide).
48
+ - Shaded (filled) bars, not an unfilled outline/step line.
49
+ - **Histograms only**:
50
+ - For continuous variables or integer variables with >= 30 bins,
51
+ suggest KDE smoothing with filled plots. Never start with smoothing;
52
+ instead, show an initial histogram without smoothing and suggest it.
53
+ - For integer variables/PMF plots that are graphed without smoothing,
54
+ ensure the number of bins aligns with the x range. Graph with shaded
55
+ bars only.
56
+
57
+ ## Square-aspect Plots (Scatter Plots, 2D Histograms)
58
+
59
+ - Aspect ratio: 1:1 (square).
60
+ - **2D Histograms only** -- include a colorbar sized to the height of
61
+ the histogram for visual clarity.
62
+
63
+ ## Common Mistakes
64
+
65
+ | Mistake | Fix |
66
+ | --- | --- |
67
+ | Describing what a figure shows without having looked at the PNG | Always `read` the figure before writing anything about what it shows |
68
+ | Summarizing a figure in prose only, without the user seeing it | `show` the PNG to the user as part of the handoff, then summarize |
69
+ | Building a plot from a compact aggregate (<=20 values) with no saved table | Save the underlying table alongside the figure -- cheap at that size |
70
+ | Inventing a per-type styling rule for a plot type with no subsection yet | Follow Defaults and ask -- don't guess a convention that hasn't been stated |
71
+ | Adding a KDE overlay to a discrete-variable histogram | KDE implies continuous density -- shaded bars only, even at cleanup time |
72
+ | Histogram bars show banding or an every-other-bar artifact | Try a different bin width/count, or overlay a KDE to check if the shape is real or a binning artifact |
73
+ | Comparing multiple histograms on one axes with different bin edges per series | Use identical bin edges across all series being compared |
74
+ | A scatter plot has so many overlapping points it's an unreadable blob | Switch to `hist2d`/hexbin (density) or drop alpha significantly, instead of a plain scatter |
@@ -0,0 +1,63 @@
1
+ # Pre-Code Checkpoints
2
+
3
+ What to do for `/skill:exploring-data` before you write any
4
+ code for the next question.
5
+
6
+ ## Scope the analysis (once per analysis, before Peek at the data)
7
+
8
+ - Before opening the data, check in one or two sentences what is broadly in
9
+ scope for this analysis and get the user's confirmation. This is a "what
10
+ are we even doing here", separate from and prior to the per-question framing
11
+ below.
12
+ - Ask the user for a report name for this analysis -- it names both the
13
+ report file and its paired folder (see `directory-structure.md`).
14
+ Don't pick one yourself.
15
+ - Keep it broad on purpose -- treat it as provisional, not a lock. Expect
16
+ it to narrow or shift as the data reveals what's actually there, well
17
+ before you reach conclusions -- confirm the new framing with the user
18
+ when it does, same as any other question (see below). `index.md` and
19
+ the report are written together during
20
+ `/skill:finishing-exploratory-data-analysis`, not during exploration.
21
+
22
+ ## Peek at the data (once per new dataset, before Propose/ask a question)
23
+
24
+ - Load the dataset (lazily), check schema/dtypes.
25
+ - Get null counts and basic summary stats (mean/median, min/max, std).
26
+ - Get a feel for the actual data: `.head()`, a strided sample, or a random
27
+ sample (fixed seed).
28
+ - Read a few raw rows by eye. They catch what schema/dtype checks and basic
29
+ stats above miss: a "numeric" column that's really a formatted string,
30
+ an ID convention, units, or what a free-text field actually holds.
31
+ - Even raw rows and basic stats can miss duplicate rows.
32
+ - Beyond that, think about what else is cheap to calculate and might
33
+ reveal something missed above -- e.g. sentinel/placeholder values (`-1`,
34
+ `0`, `"unknown"`), class imbalance, or a grain mismatch after a join.
35
+
36
+ ## Propose/ask a question (once per question, every time)
37
+
38
+ - Confirm the framing for *this* question/figure with the user before
39
+ writing a script for it.
40
+ - State the new framing in a sentence before scripting it, even if a broad
41
+ go-ahead was already given for the analysis as a whole.
42
+ - This applies to every question, including one you decide to add on your
43
+ own after an earlier go-ahead. Approval for one question is not
44
+ approval for the next one the user hasn't seen proposed yet.
45
+ - **A blanket "keep going" / "I trust your judgment" is not per-question
46
+ approval.** It authorizes you to keep working on framing, not to skip
47
+ asking about the next question's framing. Treat it the same as
48
+ silence: propose the next question's framing in one sentence, then
49
+ build it.
50
+ - Do not build multiple questions in one pass and hand over a "finished
51
+ report" -- stop after each one and surface what it shows before
52
+ moving to the next.
53
+
54
+ ## Common Mistakes
55
+
56
+ | Mistake | Fix |
57
+ | --- | --- |
58
+ | Opening the data before agreeing on a broad scope | State the analysis's scope/question area in a sentence and confirm before Peek at the data |
59
+ | Picking the report/subfolder name yourself instead of asking | Ask before creating it -- one name, shared by the file and its folder |
60
+ | Treating EDA as a pipeline and auto-advancing through questions without a framing check | Confirm framing per question, not once for the whole analysis |
61
+ | Adding a follow-up figure/question under an earlier blanket go-ahead without confirming its framing | State the new framing in a sentence before scripting it |
62
+ | Building the next two questions back-to-back and presenting a "complete report" because the user said "keep going" / "I trust your judgment" | That phrase covers continued work, not skipped per-question confirmation -- stop after each one |
63
+ | Skipping the raw-row eyeball and stopping at summary stats | Read a few actual rows -- aggregates hide format/unit/encoding surprises |
@@ -0,0 +1,66 @@
1
+ # Workflow
2
+
3
+ Loop/flow-control for `/skill:exploring-data`.
4
+
5
+ The user drives this loop: they propose or pick the next thing to plot or
6
+ check, you build it and return what it shows. They then interpret it
7
+ and pick next steps or ask additional questions.
8
+
9
+ This is a looped pipeline, exiting only when the user decides to
10
+ lock-in an analysis.
11
+
12
+ ```dot
13
+ digraph EDA_WORKFLOW {
14
+ "Start analysis" [shape=doublecircle];
15
+ "Scope the analysis" [shape=box, style=filled, fillcolor=lightblue];
16
+ "Peek at the data" [shape=box, style=filled, fillcolor=lightyellow];
17
+ "Propose/ask a question" [shape=box, style=filled, fillcolor=lightblue];
18
+ "Build script/plot" [shape=box];
19
+ "light or heavy?" [shape=diamond];
20
+ "Reread script" [shape=box];
21
+ "Dispatch script-checker (blocking)" [shape=box];
22
+ "run now, this session" [shape=box];
23
+ "Dispatch job-runner" [shape=box];
24
+ "Summarize the answer" [shape=box, style=filled, fillcolor=lightyellow];
25
+ "Continue analysis or lock in?" [shape=diamond, style=filled, fillcolor=lightblue];
26
+ "/skill:finishing-exploratory-data-analysis" [shape=doublecircle];
27
+
28
+ "Start analysis" -> "Scope the analysis";
29
+ "Scope the analysis" -> "Peek at the data";
30
+ "Peek at the data" -> "Propose/ask a question";
31
+ "Propose/ask a question" -> "Build script/plot";
32
+ "Build script/plot" -> "light or heavy?";
33
+ "light or heavy?" -> "Reread script" [label="light"];
34
+ "Reread script" -> "Build script/plot" [label="issues found"];
35
+ "Reread script" -> "run now, this session" [label="looks right"];
36
+ "light or heavy?" -> "Dispatch script-checker (blocking)" [label="heavy"];
37
+ "Dispatch script-checker (blocking)" -> "Build script/plot" [label="issues found"];
38
+ "Dispatch script-checker (blocking)" -> "Dispatch job-runner" [label="clean"];
39
+ "run now, this session" -> "Summarize the answer";
40
+ "Dispatch job-runner" -> "Summarize the answer";
41
+ "Summarize the answer" -> "Continue analysis or lock in?";
42
+ "Continue analysis or lock in?" -> "Propose/ask a question" [label="next question"];
43
+ "Continue analysis or lock in?" -> "/skill:finishing-exploratory-data-analysis" [label="finish analysis"];
44
+ }
45
+ ```
46
+
47
+ (blue = User turn, yellow = Agent -> User handoff, white = Agent-only)
48
+
49
+ | Stage | Turn | What it looks like | See |
50
+ | --- | --- | --- | --- |
51
+ | Scope the analysis | User | State the broad scope/question area for this analysis and confirm it, once per analysis, before touching data | `pre-code-checkpoints.md` (Scope the analysis) |
52
+ | Peek at the data | Agent -> User | Schema/dtypes, `.head()`, a sample, eyeball raw rows, hand back for a look | `pre-code-checkpoints.md` (Peek at the data) |
53
+ | Propose/ask a question | User (agent may suggest) | Confirm framing for the next question, then the user picks what to plot or check | `pre-code-checkpoints.md` (Propose/ask a question) |
54
+ | Build script/plot | Agent | Write the script/plot, smoke test on a subset before scaling | `checking-and-running-scripts.md`, `plotting.md` |
55
+ | light or heavy? | Agent | Judge from the smoke-test timing/size ratio | `checking-and-running-scripts.md` |
56
+ | Reread script | Agent | Light: reread the script yourself before running -- no `script-checker` dispatch, loop back to the script on issues you find | `checking-and-running-scripts.md` |
57
+ | Dispatch script-checker (blocking) / Dispatch job-runner | Agent | Heavy: dispatch the `script-checker` and wait; loop back to the script on findings; don't launch the `job-runner` until it's clean | `checking-and-running-scripts.md` |
58
+ | Summarize the answer | Agent -> User | Interpret the figure/stats as a summary, not a conclusion -- the user decides what it means and what's next | `SKILL.md` Core principle |
59
+ | Lock-in checkpoint | User | Figure/analysis seems meaningful, ready to write up or commit | `/skill:finishing-exploratory-data-analysis` |
60
+
61
+ ## Common Mistakes
62
+
63
+ | Mistake | Fix |
64
+ | --- | --- |
65
+ | Treating any issue in scripts (via self-reread or `script-checker`) as a note to fold in later instead of a loop back to the script | Go to "Build script/plot" and fix it -- see the graph's "issues found" edges |
66
+ | Launching a heavy scale-up while the `script-checker` is still pending or has open findings | See `checking-and-running-scripts.md`'s Common Mistakes |
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: finishing-exploratory-data-analysis
3
+ description: Use when a figure or EDA analysis seems meaningful and ready to summarize, write into a report, or commit
4
+ ---
5
+
6
+ > **Related skills:** Use `/skill:exploring-data` for the exploration/plotting phase this follows. Use `/skill:verification-before-completion` before treating any figure as a finished result. Use `/skill:pi-subagents` (`runs.all`, `context: "fresh"`/`"fork"`) for the two dispatches in `verification-dispatch.md`.
7
+
8
+ # Finishing Exploratory Data Analysis
9
+
10
+ ## Overview
11
+
12
+ Locking in a figure/analysis is a sequential, one-at-a-time gate, not a
13
+ batch step at the end of a session.
14
+
15
+ **Core principle:** never write to the report or commit from your own
16
+ read of a figure -- confirm the story with the user first, every time. A
17
+ broader go-ahead, or the user saying "ship it" about the figure itself,
18
+ confirms the figure's story -- not this unrun verification step.
19
+
20
+ Once the report is written or updated, don't skip straight to offering to
21
+ commit -- run the verification dispatch (step 4) first. A completed code
22
+ review doesn't count (see Common Mistakes).
23
+
24
+ ## Lock-in Checkpoint (do this once a figure/analysis seems meaningful)
25
+
26
+ 1. **Walk through the figures with the user.** `show` each figure and
27
+ re-examine it together to confirm the narrative -- no written artifact
28
+ yet.
29
+ 2. **Summarize and wait.** Give the user a ~4 sentence written summary
30
+ of the agreed narrative. Wait for confirmation before proceeding.
31
+ 3. **Write the report and `index.md`.** Once confirmed, add or update
32
+ the `index.md` and report (`<report_name>.md`, sibling to its paired
33
+ `<report_name>/` scripts/figs folder). Create them here if necessary.
34
+ - Writing style: clear sentences. No run-ons, reduce em/en-dash
35
+ (`--`/`—`) as a sentence joiner. Split into two sentences or use a
36
+ comma/colon instead.
37
+ - `index.md` contents -- a brief description (no figures) containing:
38
+ - *Link to report*
39
+ - *Brief summary*
40
+ - *Scope of analysis*
41
+ - *Dataset description*
42
+ - *Open questions*, if any
43
+ - Report contents -- at minimum:
44
+ - *Scope of analysis*
45
+ - *Data source(s) used*
46
+ - *Questions*, grouped when closely related:
47
+ - *Approach* in prose, with key equations if nontrivial
48
+ - *Results*, with the embedded figure(s)
49
+ - *Conclusion*
50
+ 4. **Reread, then dispatch verification.** See `verification-dispatch.md`
51
+ for the reread pass, the `naive-reader`/`critical-auditor` dispatch, and
52
+ the severity rubric for findings.
53
+ 5. **Integrate findings and modify the report.** Apply silent fixes per
54
+ the rubric; surface material-tier and `critical-auditor` findings
55
+ verbatim, flagged unresolved if not yet addressed. If the fix is
56
+ substantive, ask the user whether to rerun verification.
57
+ 6. **Ask before committing.** Explicitly ask if the user is ready to
58
+ commit -- don't commit on your own judgment.
59
+
60
+ ## Common Mistakes
61
+
62
+ | Mistake | Fix |
63
+ | --- | --- |
64
+ | Writing to the report from your own interpretation of a figure | Confirm with a ~4 sentence summary first |
65
+ | Putting figures/results into `index.md` | Keep it a skimmable summary of what's been done -- figures and results belong in the report files |
66
+ | Dispatching verification before the report is written | Verification checks the written report against the figures/script -- write first (step 3), then dispatch (step 4) |
67
+ | Stating a correlation as if it were causal | Hedge appropriately in report prose; correlation is not causation |
68
+ | Silently patching a material-tier or `critical-auditor` finding before telling the user | Always surface these verbatim in the summary, even unresolved -- only cosmetic/confusing/non-fabricated-clarifying fixes get silently applied |
69
+ | Treating a completed script/code review as satisfying this checkpoint's dispatch | Different gate, different artifact -- a code review checks the script, this checkpoint checks the written report. See `verification-dispatch.md` |
70
+ | Saying an analysis is "done and reviewed" once code review passes, before the lock-in checkpoint has run | Say so explicitly ("script validated; report still needs the lock-in checkpoint") rather than a blanket "done" that could be read as covering sign-off too |
@@ -0,0 +1,51 @@
1
+ # Verification Dispatch
2
+
3
+ The critical checkpoint in `/skill:finishing-exploratory-data-analysis`:
4
+ reread the report/script, then dispatch subagents to check it, before
5
+ you offer to commit. Together the two dispatches establish trust in
6
+ the report: that it's clear and complete as a standalone artifact
7
+ (`naive-reader`), and that it was produced with the correct semantic
8
+ intent (`critical-auditor`). See each agent's own definition in this
9
+ package's `agents/` directory (`naive-reader.md`, `critical-auditor.md`)
10
+ for what they check and how they're configured (context, tools).
11
+
12
+ 1. **Reread first.** Reread the report/script yourself for leftover
13
+ editing artifacts (draft asides, TODO notes, contradictions) before
14
+ dispatching -- don't hand reviewers an unread draft.
15
+ 2. **Dispatch both in parallel** (`runs.all`), every lock-in, not just
16
+ risky ones:
17
+
18
+ ```js
19
+ runs.all([
20
+ {key:"naive", agent:"naive-reader", task:"<report, figures, index.md>"},
21
+ {key:"audit", agent:"critical-auditor", task:"<report, figures, numbered scripts>"},
22
+ ])
23
+ ```
24
+
25
+ If fork context isn't available for `critical-auditor`, hand it a
26
+ written recap of this session's framing/decisions in the task instead
27
+ -- context-free it's just a second `naive-reader`.
28
+
29
+ ## Handling `naive-reader` findings by tier
30
+
31
+ | Tier | Handling |
32
+ | --- | --- |
33
+ | cosmetic | fix silently, one-line changelog note |
34
+ | confusing | fix silently (simplify/rewrite), one-line changelog note |
35
+ | clarifying | fix silently if you actually know the answer. If closing the gap means guessing or inventing a reason, don't fabricate it -- tell the user honestly instead |
36
+ | material | always surface, never silently patched -- this is a correctness signal wearing a comprehension-test disguise |
37
+
38
+ ## Handling `critical-auditor` findings
39
+
40
+ Findings have no tiers: everything it reports is drift between intent
41
+ and implementation, never silently resolved -- goes into the
42
+ user-facing summary verbatim.
43
+
44
+ ## Common Mistakes
45
+
46
+ | Mistake | Fix |
47
+ | --- | --- |
48
+ | Skipping the `naive-reader`/`critical-auditor` dispatch because it "seems fine" | Every lock-in, no exceptions -- this is the final check before commit, and "seems fine" is exactly the curse-of-knowledge blind spot it exists to catch |
49
+ | Inventing a plausible-sounding rationale to resolve a clarifying-tier gap you don't actually know the answer to | Surface it as an open question instead -- never fabricate reasoning to make a flag disappear |
50
+ | Letting the `critical-auditor` run with no conversation context because fork wasn't available | Hand it an explicit written recap of the framing/decisions instead -- context-free, it's just a second `naive-reader` and can't catch intent drift |
51
+ | Treating a completed script/code review as satisfying this dispatch | Different gate, different artifact -- a code review checks the script; this dispatch checks the written report |