Lightweight Governance for Coding Assistants
Whether we like it or not, coding assistants are now just part of how we write code. Trying to “fight gravity” and ban them is usually counterproductive—you just end up slower than everyone else. But leaning too far in the other direction and letting an AI agent run wild in your repository is equally dangerous.
To get the leverage of an assistant without sacrificing the integrity of your codebase, you need lightweight project-level guardrails. You need to explicitly tell the assistant how your project is structured and what it is (and isn’t) allowed to do.
Why Project Rules Matter
If you drop an unguided assistant into a repository, it will default to its training data’s average behavior. In the Python data ecosystem, that means chaos. Ask an assistant to add a library, and it might generate a requirements.txt, create a conda environment, or suggest poetry—all in the same project. Ask it to build a baseline model, and it might reach for PyTorch when a simple scikit-learn logistic regression is all you need. Left to its own devices, it will create ad-hoc scripts/ or temp/ folders whenever it feels like it.
We don’t want chaos. As we established in the previous foundation piece, we want our ML projects to be small, boring, and reproducible. We standardized our environment management around uv; now we need to standardize our assistant’s behavior around our project constraints.
One Set of Rules, Many Assistants
The trick to good governance is keeping your rules generic and tool-agnostic. You should never have to rewrite your project’s architectural decisions just because you switched from Claude to Copilot.
To solve this, I use a “Single Source of Truth” pattern. All of my core architectural decisions live in a single, plain-English Markdown file called ml-classic.md.
Then, for every assistant I use, I create that specific assistant’s required “entry” file (like CLAUDE.md). Instead of duplicating all my rules, that entry file simply points the assistant to read ml-classic.md.
For example, a typical entry file snippet looks like this:
This repository uses `uv` for environments and follows the rules in ml-classic.md.
Please read that file before suggesting code changes.
This keeps the “assistant” from becoming the master of the project. The repository dictates the rules; the assistants merely read them.
What Lives in ml-classic.md
The ml-classic.md file acts as the constitution for the repository. It lays out the boundaries that the assistant is not allowed to cross. Because it’s written in plain, tool-agnostic English, any modern LLM can read and follow it.
My ML foundations rulebook covers four main areas:
- Project Skeleton: It explicitly defines the allowed directory structure, preventing the assistant from creating unexpected folders.
- Environment: It strictly enforces
uvas the only dependency manager and explicitly banspiporcondacommands. - Workflow: It defines when to use Jupyter notebooks (for exploration) versus Python scripts (for reusable components).
- Scope: It restricts the tech stack to classic ML (pandas, scikit-learn) and explicitly forbids pulling in deep learning frameworks for simple baselines.
I provide a visual map of the allowed folder structure right in the file, which the LLM uses as a strict reference constraint:
my-ml-project/
├── data/ # Raw and processed data
├── notebooks/ # Numbered exploration notebooks (01_, 02_)
├── src/ # Reusable Python modules
└── reports/ # Generated figures and metrics
You can see the full, real-world file in my public repository here:
ml-classic.md
Concrete Setup for the Three Assistants
With the core rulebook written, hooking it up to the three major coding assistants is trivial.
Antigravity
Antigravity natively looks for a GEMINI.md file at the root of your workspace. My file is incredibly short. It simply establishes the baseline context and immediately delegates the heavy lifting to the master rulebook.
# Antigravity Instructions
You are assisting with a classic Machine Learning project. Read the `ml-classic.md` file for strict repository rules.
See the real GEMINI.md file here.
Claude Code
Claude Code relies on a CLAUDE.md file at the project root. While it also delegates to the master rulebook, I include a very brief bulleted summary of the absolute most critical rules (like “Use uv”) to ensure they remain pinned in Claude’s immediate context window.
# Claude Code Guidelines
- Environment: Use `uv` strictly. Do not use pip or conda.
- Rules: All architectural constraints are in `ml-classic.md`. Read it before starting work.
See the real CLAUDE.md file here.
GitHub Copilot
GitHub Copilot handles custom instructions via a .github/copilot-instructions.md file. The pattern remains exactly the same. We give it a short preamble and point it directly back to our single source of truth.
# Copilot Instructions
This is a classic ML repository. You must adhere to the folder structure and tool constraints defined in `ml-classic.md`.
See the real .github/copilot-instructions.md file here.
By structuring it this way, all three assistants are reading the exact same policy, ensuring your project behavior is completely consistent regardless of which AI is currently typing.
Why This Matters Long-Term
Writing a file to govern your AI assistant might feel like busywork, but it is a critical piece of modern engineering foundation. Setting up governance before you start coding prevents the chaotic, after-the-fact cleanup that happens when an assistant hallucinates five different, conflicting build tools into your repository.
More importantly, this setup keeps the project in charge, not the tool. Swapping or turning off an assistant doesn’t change how your project is structured. If a brand-new, better assistant comes out tomorrow, you don’t have to rewrite your architecture or your prompts. You just create a new entry file that points to ml-classic.md, and you are immediately ready to get back to work.
Next Steps
A robust machine learning workflow requires building layers of trust. First, we locked down our environment management so our code runs reliably anywhere. Second, we established project-level governance so our AI assistants write code that strictly aligns with our constraints.
With these operational foundations out of the way, we are finally ready to start doing actual machine learning. In the later essays, we’ll dive straight into the code—building robust baseline models and setting up proper evaluation frameworks.
ML Classics
Related Notes
My Super Simple ML Workbench (That Covers ~80% of Classic ML)
For most tabular ML work — loading CSVs, training scikit-learn baselines, plotting results — you need surprisingly little tooling. Here's the boring, reproducible workbench I actually use: uv for environments and deps, VS Code for notebooks and scripts, and nothing else.
Evaluating ML Models: It’s About Choosing Your Mistakes
When we talk about evaluating ML models, we often jump straight to metrics. But that skips the real question: What kind of mistakes can your system afford to make?