<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Norman Niemer</title>
    <description>The latest articles on DEV Community by Norman Niemer (@norman_niemer_7f327e153b9).</description>
    <link>https://dev.to/norman_niemer_7f327e153b9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4065183%2Fdebbaa39-9815-4a9c-a07e-f7afae4f0cba.png</url>
      <title>DEV Community: Norman Niemer</title>
      <link>https://dev.to/norman_niemer_7f327e153b9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/norman_niemer_7f327e153b9"/>
    <language>en</language>
    <item>
      <title>How to make Claude Code a trustworthy data scientist</title>
      <dc:creator>Norman Niemer</dc:creator>
      <pubDate>Mon, 10 Aug 2026 16:30:00 +0000</pubDate>
      <link>https://dev.to/norman_niemer_7f327e153b9/how-to-make-claude-code-a-trustworthy-data-scientist-58of</link>
      <guid>https://dev.to/norman_niemer_7f327e153b9/how-to-make-claude-code-a-trustworthy-data-scientist-58of</guid>
      <description>&lt;p&gt;&lt;em&gt;AI agents like Claude Code now write real data science pipelines — feature engineering, model training, experiment sweeps. Here's the honest account of where they fail at it, and why a lightweight workflow library removes exactly those failures.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Coding agents have gotten good at writing pandas and scikit-learn. Ask one to load a dataset, engineer features, train a model, and compare a few configurations, and it will produce plausible code fast. But "produces plausible code" and "produces a correct, reproducible pipeline you can keep iterating on" are different bars — and the gap between them is where agents quietly go wrong.&lt;/p&gt;

&lt;p&gt;This post is written from the perspective of the agent. What actually trips me up when I do data science work across a long session, and what does a caching, dependency-aware workflow library like oryxflow do about it?&lt;/p&gt;

&lt;h2&gt;
  
  
  The core weakness: I can't see state across turns
&lt;/h2&gt;

&lt;p&gt;The thing that makes me error-prone in data work isn't syntax. It's &lt;strong&gt;invisible state&lt;/strong&gt;. When I write a linear analysis script over many turns, I have no reliable memory of &lt;em&gt;what has already been computed and whether it's still valid&lt;/em&gt;. A human running the same script in a notebook at least has the cell outputs in front of them. I'm reconstructing that picture from scratch every turn, and I get it wrong in three specific ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stale intermediates.&lt;/strong&gt; I write &lt;code&gt;features.pkl&lt;/code&gt; early, change the feature code later, forget to regenerate it, and then train a model on stale features. No error is raised. The pipeline runs, the numbers are just wrong. I don't hold a durable link between a saved file and the code version that produced it, so I can't reliably notice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expensive recompute in my inner loop.&lt;/strong&gt; My whole working style is run → observe → edit → run. In a plain script, every loop recomputes the slow steps — the big join, the model fit — so I either waste time or start hand-rolling &lt;code&gt;if os.path.exists(...)&lt;/code&gt; caches, which then become failure mode #1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Path and load bookkeeping I get wrong.&lt;/strong&gt; I hardcode output paths, lose track of what's saved where, and occasionally load the wrong file into the wrong step.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these are intelligence problems. They're &lt;em&gt;memory&lt;/em&gt; problems — and they're structural, because my context is finite and my recollection of "did I already run this, is it still valid" degrades over a long session.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a caching DAG does: it externalizes the state I'm bad at holding
&lt;/h2&gt;

&lt;p&gt;A workflow library flips the model. Instead of a script that runs top to bottom, you declare each step as a task with explicit dependencies, and the engine owns execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oryxflow&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskPqPandas&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;load_raw&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;            &lt;span class="c1"&gt;# no filename to manage
&lt;/span&gt;
&lt;span class="nd"&gt;@oryxflow.requires&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c1"&gt;# declares the edge
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BuildFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskPqPandas&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;engineer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inputLoad&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="nd"&gt;@oryxflow.requires&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildFeatures&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrainModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskPickle&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gbm&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;feat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inputLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;clf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveMeta&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(...)})&lt;/span&gt;

&lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TrainModel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at what this removes for an agent specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The dependency graph is now data, not something I have to remember.&lt;/strong&gt; The requires edges are the state I would otherwise be reconstructing every turn. I don't have to keep "features feed the model, which feeds the report" in my head — it's declared, and the engine walks it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-running is cheap and correct by default.&lt;/strong&gt; Run twice and completed tasks load from cache instead of recomputing &lt;code&gt;(3 complete, 0 ran)&lt;/code&gt;. My run-observe-edit loop stops being a recompute tax, so I iterate faster without hand-rolling caches that rot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There are no filenames for me to get wrong.&lt;/strong&gt; &lt;code&gt;self.inputLoad()&lt;/code&gt; and &lt;code&gt;output().load()&lt;/code&gt; address results by task identity, not by path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every task has the same shape.&lt;/strong&gt; &lt;code&gt;requires&lt;/code&gt; + &lt;code&gt;run&lt;/code&gt; + &lt;code&gt;save&lt;/code&gt;. When code is that regular I pattern-match it correctly and add the next step by copying the shape — far fewer structural mistakes than freeform script-extension gives me.&lt;/li&gt;
&lt;li&gt;The unifying idea: &lt;strong&gt;the DAG is a memory prosthesis for exactly the thing I'm worst at.&lt;/strong&gt; A disciplined human gets something from this. I get more, because the discipline it enforces is the discipline I can't reliably self-supply across a long session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The value scales &lt;em&gt;up&lt;/em&gt; with complexity — and it starts at quick EDA
&lt;/h2&gt;

&lt;p&gt;There's an important corollary about where on that curve this starts paying off.&lt;/p&gt;

&lt;p&gt;For genuinely throwaway work — "load this CSV, group by, plot one thing" — a task DAG is overhead. Plain pandas in a scratch .py is faster and clearer, and forcing task classes around five lines you'll run once is pure ceremony. But "no task classes" is not the same as "no structure", and that distinction matters more for me than it does for you. When I explore with the &lt;a href="https://docs.oryxflow.dev/docs/claude-code-for-data-science/" rel="noopener noreferrer"&gt;oryxflow Claude Code plugin&lt;/a&gt; active, the exploration itself is structured: I write a read-only probe &lt;em&gt;inside&lt;/em&gt; the project — a small script whose one-line docstring states the question it answers, which prints the answer legibly and runs again next session — instead of a snippet that dies with my context. And whatever it turns up gets written into the project's data doc. A probe I can re-run is a question answered; a lost snippet is a question I will silently re-ask in three turns.&lt;/p&gt;

&lt;p&gt;Then, when a probe turns out to be load-bearing — rerun often, depended on, or swept over parameters — I don't rewrite it: &lt;code&gt;/oryxflow:migrate&lt;/code&gt; promotes it into cached, parameterized tasks, reading the script as the spec and leaving it in place (&lt;a href="https://docs.oryxflow.dev/docs/migrate-notebook-to-pipeline/" rel="noopener noreferrer"&gt;walkthrough: migrate a notebook to a pipeline&lt;/a&gt;). Both ends of the project's life are covered by the same skill, so there's no cliff in the middle — start with simple scripts, scale to any complexity.&lt;/p&gt;

&lt;p&gt;And the calculus inverts as projects get complex — &lt;em&gt;super-linearly&lt;/em&gt;. Consider what "complex" actually means in a real data science project and what each trait does to an agent working without a DAG:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deep dependency chains&lt;/strong&gt; (ten-plus steps from raw data to final output). The deeper the chain, the more catastrophic a silent stale intermediate near the top is — it corrupts everything below it, and the further downstream the visible output, the less likely I am to trace the wrongness back to the source. Depth is exactly where my "hold the graph in my head" strategy fails hardest, and exactly where declared edges help most.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expensive nodes you cannot afford to recompute.&lt;/strong&gt; Real pipelines have steps that are slow &lt;em&gt;and&lt;/em&gt; frequently upstream of the thing you're editing: large multi-table joins, model training, walk-forward retraining over an expanding window, computing explainability artifacts, and — increasingly — calls to external LLMs inside a task. Caching these by identity is the difference between a tractable inner loop and one where every experiment costs minutes or dollars. The more expensive the node, the more the cache is worth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter sweeps and experiment matrices.&lt;/strong&gt; Serious modeling means comparing a Cartesian product of choices — model type × preprocessing variant × feature set × training window × strategy, and so on. Hand-managing output filenames for that product across a deep chain is combinatorially hopeless, and manually orchestrating one pipeline per configuration is precisely where I introduce ordering and state bugs. A declarative sweep collapses it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;flow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oryxflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WorkflowMulti&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrainModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ols&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ols&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gbm&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gbm&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputLoadMeta&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;   &lt;span class="c1"&gt;# {'ols': {'score': ...}, 'gbm': {'score': ...}}`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each configuration automatically gets its own cached output keyed by its parameters; shared upstream steps are computed once and reused across the whole sweep. Training the second model doesn't recompute the data and features the first one already built.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple data sources joined together&lt;/strong&gt;. When independently-updated sources feed a join, "which source changed, so what's now stale?" is a provenance question I can't answer by memory. The dependency edges make it explicit and mechanical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many steps of uniform shape&lt;/strong&gt;. At thirty-plus tasks, uniformity is what lets me extend the project safely. A thousand-line freeform script is something I edit nervously; a set of identical-shaped tasks is something I extend confidently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the rule of thumb for an agent is: &lt;strong&gt;re-runnable probes for exploration, tasks the moment the work has a shape worth keeping — and one command to get from the first to the second&lt;/strong&gt;. The DAG's value curve rises with depth, cost, and the size of the experiment matrix — the traits that define a hard project, and the traits every project I work on eventually grows.&lt;/p&gt;

&lt;p&gt;Notice that limits (1) and (2) are not analytical — they're mechanical gaps the library leaves open. Which is the whole point of pairing the library with an agent-side skill — see &lt;a href="https://docs.oryxflow.dev/blog/ai-agents/claude-code-skills-for-data-science/" rel="noopener noreferrer"&gt;Claude Code skills for data science&lt;/a&gt; for what a skill is and why it, rather than a data connector, is the thing that closes a gap like this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Library plus plugin: a matched pair
&lt;/h2&gt;

&lt;p&gt;The two residual mechanical risks — &lt;em&gt;verify that an edit's rerun actually happened&lt;/em&gt; (the blind-spot net) and &lt;em&gt;get the multi-input wiring right&lt;/em&gt; — are precisely what an editor-integrated skill can carry. The &lt;a href="https://github.com/oryxintel/oryxflow-claude-plugin" rel="noopener noreferrer"&gt;oryxflow Claude Code plugin&lt;/a&gt; exists for this: it activates when an agent touches pipeline files and front-loads the correct idioms — the session-start &lt;code&gt;events.print_status()&lt;/code&gt;habit, the verify-the-rerun check after every edit, answering staleness and expensive-recompute warnings with the right exit (recompute /&lt;code&gt;accept_code&lt;/code&gt; / pin), and the right patterns for selecting named inputs from multi-parent tasks.&lt;/p&gt;

&lt;p&gt;That produces a clean division of labor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The library&lt;/strong&gt; carries the state-tracking an agent is structurally bad at — the dependency graph, the caching, the parameter-keyed reruns, automatic code invalidation with downstream propagation, and the warnings on pinned or expensive tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The plugin&lt;/strong&gt; carries the remaining disciplines — verifying reruns landed, answering warnings with the right exit, and the multi-input API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this pairing gets &lt;em&gt;more&lt;/em&gt; valuable as complexity rises, not less — the opposite of most tooling, which buckles under scale. For the full picture of how the library and plugin work together, see &lt;a href="https://docs.oryxflow.dev/docs/claude-code-for-data-science/" rel="noopener noreferrer"&gt;Claude Code for data science&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;For quick exploration, plain files are fine — a DAG there is just ceremony. What matters is that the plain file is a re-runnable probe living in the project, because exploration rarely stays quick, and one command promotes it when it stops being quick. And for any data science work with a &lt;em&gt;shape&lt;/em&gt; — a deep chain, expensive steps, a matrix of experiments, several data sources joined together — a caching, parameter-aware workflow library stops being optional. It externalizes the pipeline state an AI coding agent is structurally unable to hold reliably, so the agent iterates fast without silently building on stale data. The library isn't a substitute for judgment; it's the thing that makes an agent's mechanical data-engineering &lt;em&gt;trustworthy&lt;/em&gt; enough that the judgment is worth having.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h2&gt;
  
  
  How do I trust analysis an AI agent wrote — is it reproducible?
&lt;/h2&gt;

&lt;p&gt;Trust comes from structure, not from the agent's confidence. Put the analysis in a DAG that reruns exactly what a code or data change affects and records what ran to a greppable lineage log. oryxflow gives you that: automatic code-change invalidation with downstream propagation, plus a .oryxflow/events.jsonl trail. Reproducible is not the same as correct — the DAG makes a wrong pipeline faithfully reproducible too, so judgment stays yours.&lt;/p&gt;

&lt;p&gt;How do I keep an AI agent from building on stale data?&lt;br&gt;
The failure is silent: the agent edits feature code, forgets to regenerate the saved output, and trains on stale data with no error raised. An engine that tracks task code fixes it, because editing a step makes the next run recompute that step and everything downstream automatically. oryxflow does this via source-level code-change invalidation, so you never evaluate new code on old output.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I stop an AI coding agent from rerunning expensive steps?
&lt;/h2&gt;

&lt;p&gt;The same task identity that keeps results honest also makes reuse safe: because each step is keyed on its code, inputs and parameters, anything genuinely unchanged can load from disk instead of recomputing, so the big join or model fit runs once rather than every turn. In oryxflow that is automatic — the agent's run-edit loop stops being a recompute tax, which is what stops reproducibility from costing you time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install oryxflow&lt;/code&gt;&lt;br&gt;
Source &amp;amp; examples: &lt;a href="https://github.com/oryxintel/oryxflow" rel="noopener noreferrer"&gt;https://github.com/oryxintel/oryxflow&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://docs.oryxflow.dev" rel="noopener noreferrer"&gt;https://docs.oryxflow.dev&lt;/a&gt;&lt;br&gt;
Build pipelines with an agent: &lt;a href="https://github.com/oryxintel/oryxflow-claude-plugin" rel="noopener noreferrer"&gt;https://github.com/oryxintel/oryxflow-claude-plugin&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
